F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
WasmSequencerInterpreter.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title WasmSequencerInterpreter.cpp
3 // \author tumbar
4 // \brief cpp file for WasmSequencer engine state machine
5 // ======================================================================
6 
7 #include "Fw/Com/ComPacket.hpp"
8 #include "Fw/Types/Assert.hpp"
17 #include "spacewasm.h"
18 
19 namespace Svc {
20 
21 // ----------------------------------------------------------------------
22 // Implementations for internal state machine actions
23 // ----------------------------------------------------------------------
24 
25 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_cmdReplyOK(
26  SmId smId,
28  const Svc::WasmSequencer_CommandRequest& value) {
30 }
31 
32 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_signalEntered(
33  SmId smId,
36 }
37 
38 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_spin(
39  SmId smId,
41  FW_ASSERT(this->m_wasm);
42 
43  Fw::ParamValid prmValid;
44  const auto fuelParam = this->paramGet_INSTRUCTION_FUEL(prmValid);
45  // Min of 1 so we don't spin the component queue forever
46  const auto fuel = (fuelParam == 0) ? 1 : fuelParam;
47 
49  const spacewasm_run_status_t runStatus = spacewasm_run(this->m_wasm, fuel, &trap);
50 
51  switch (runStatus) {
53  spacewasm_value_t result;
54  auto status = spacewasm_get_result(this->m_wasm, spacewasm_valtype_t::SPACEWASM_I32, &result);
55  if (status == SPACEWASM_ERR_NOT_FOUND) {
56  // Return status code was "void" (success)
58  } else {
59  FW_ASSERT(status == SPACEWASM_OK);
62  }
63 
64  break;
65  }
66  case SPACEWASM_RUN_TRAP:
67  // Filter out HOST traps due to exit/panic. These host functions just use trap as a mechanism
68  // to kill the interpreter.
69  if (trap == SPACEWASM_TRAP_HOST && (this->m_exit.reason == WasmSequencer_ExitReason::HOST_EXIT ||
70  this->m_exit.reason == WasmSequencer_ExitReason::HOST_PANIC)) {
72  } else {
73  this->interpreter_sendSignal_interpreterTrap(WasmSequencer::mapTrapReason(trap));
74  }
75  break;
78  break;
81  break;
82  default:
83  FW_ASSERT(false, runStatus);
84  break;
85  }
86 }
87 
88 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_reset(
89  SmId smId,
91  if (this->m_wasm != nullptr) {
92  auto status = spacewasm_reset(this->m_wasm);
93  FW_ASSERT(status == SPACEWASM_OK);
94  } else {
95  FW_ASSERT(signal == Svc_WasmSequencer_InterpreterStateMachine::Signal::__FPRIME_INITIAL_TRANSITION);
96  }
97 }
98 
99 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearExitStatus(
100  SmId smId,
102  this->m_exit.reason = WasmSequencer_ExitReason::UNKNOWN;
103  this->m_exit.lastHostFunction = WasmSequencer_HostFunction::NONE;
104  this->m_exit.code = 0;
105  this->m_exit.lastTrapReason = WasmSequencer_TrapReason::NONE;
106 }
107 
108 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_INTERPRETER_FINISHED(
109  SmId smId,
112 }
113 
114 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_INTERPRETER_TRAP(
115  SmId smId,
117  if (this->m_exit.reason == WasmSequencer_ExitReason::UNKNOWN) {
118  this->m_exit.reason = WasmSequencer_ExitReason::INTERPRETER_TRAP;
119  }
120 }
121 
122 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_REPLY_TIMEOUT(
123  SmId smId,
125  this->m_exit.reason = WasmSequencer_ExitReason::REPLY_TIMEOUT;
126 }
127 
128 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_HOST_FAILURE(
129  SmId smId,
131  this->m_exit.reason = WasmSequencer_ExitReason::HOST_FAILURE;
132 }
133 
134 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_UNEXPECTED_REPLY(
135  SmId smId,
137  this->m_exit.reason = WasmSequencer_ExitReason::UNEXPECTED_REPLY;
138 }
139 
140 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_TIMER_INCOMPARABLE(
141  SmId smId,
143  this->m_exit.reason = WasmSequencer_ExitReason::TIMER_INCOMPARABLE;
144 }
145 
146 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitReason_CANCEL(
147  SmId smId,
149  this->m_exit.reason = WasmSequencer_ExitReason::CANCEL;
150 }
151 
152 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setExitCode(
153  SmId smId,
155  I32 value) {
156  this->m_exit.code = value;
157 }
158 
159 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setTrapReason(
160  SmId smId,
162  const Svc::WasmSequencer_TrapReason& value) {
163  this->m_exit.lastTrapReason = value;
164 }
165 
166 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setLastHostFunction(
167  SmId smId,
169  this->m_exit.lastHostFunction = this->m_pendingHostFunction.kind;
170 }
171 
172 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_finish(
173  SmId smId,
175  this->controller_sendSignal_engineFinished(this->m_executingContext);
176 }
177 
178 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_reportPaused(
179  SmId smId,
182 }
183 
184 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearPause(
185  SmId smId,
187  this->m_pendingPause = false;
188 }
189 
190 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_dispatchPendingHostFunction(
191  SmId smId,
193  switch (this->m_pendingHostFunction.kind) {
195  // Invalid host function
196  FW_ASSERT(false);
197  break;
199  this->dispatchCommand();
200  break;
202  this->dispatchTelemetry();
203  break;
205  this->dispatchParameter();
206  break;
208  this->dispatchEvent();
209  break;
211  this->dispatchRelativeSleep();
212  break;
214  this->dispatchAbsoluteSleep();
215  break;
217  this->dispatchTime();
218  break;
220  this->dispatchSerialOut();
221  break;
223  this->dispatchSerialRecv();
224  break;
225  }
226 }
227 
228 Fw::Success WasmSequencer ::readGuestMemory(WasmSequencer_HostFunction::T kind, U32 addr, U8* dst, FwSizeType len) {
229  const spacewasm_status_t status = spacewasm_mem_read(this->m_pendingHostFunction.caller, addr, dst, len);
230  if (status != SPACEWASM_OK) {
231  this->log_WARNING_HI_HostFunctionInvalidPointer(kind, static_cast<WasmSequencer_Status::T>(status));
232  return Fw::Success::FAILURE;
233  } else {
234  return Fw::Success::SUCCESS;
235  }
236 }
237 
238 Fw::Success WasmSequencer ::writeGuestMemory(WasmSequencer_HostFunction::T kind,
239  U32 addr,
240  const U8* src,
241  FwSizeType len) {
242  const spacewasm_status_t status = spacewasm_mem_write(this->m_pendingHostFunction.caller, addr, src, len);
243  if (status != SPACEWASM_OK) {
244  this->log_WARNING_HI_HostFunctionInvalidPointer(kind, static_cast<WasmSequencer_Status::T>(status));
245  return Fw::Success::FAILURE;
246  } else {
247  return Fw::Success::SUCCESS;
248  }
249 }
250 
251 // ----------------------------------------------------------------------
252 // Per-host-function dispatch helpers (arms of dispatchPendingHostFunction)
253 // ----------------------------------------------------------------------
254 
255 void WasmSequencer ::dispatchCommand() {
256  Fw::ComBuffer cmd;
257 
258  // Write the CMD descriptor to the ComBuffer
259  auto serStatus = cmd.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
260  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
261 
262  // Copy the com buffer from the guest memory into our memory
263  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::COMMAND, this->m_pendingHostFunction.u.command.ptr,
264  cmd.getBuffAddr() + sizeof(FwPacketDescriptorType),
265  this->m_pendingHostFunction.u.command.len) != Fw::Success::SUCCESS) {
267  return;
268  }
269 
270  // Memory read succeeded, update the ComBuffer to hold the encoded command
271  serStatus = cmd.moveSerToOffset(this->m_pendingHostFunction.u.command.len + sizeof(FwPacketDescriptorType));
272  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
273 
274  // Dispatch command to CmdDisp. The command context (cmdUid)
275  // encodes the current sequence + command instance so we can
276  // reject late/stale responses in cmdResponseIn_handler.
277  this->m_tlm.commandsDispatched++;
278 
279  // Start the host-function timeout clock: we are about to block in
280  // AWAITING_RESPONSE until the command response comes back in.
281  this->m_hostFunctionStart = this->getTime();
282  this->m_hasHostFunctionStart = true;
283 
284  this->cmdOut_out(0, cmd, this->makeCmdUid());
285 }
286 
287 void WasmSequencer ::dispatchTelemetry() {
288  Fw::Time time;
289  Fw::TlmBuffer tlmBuffer;
290 
291  auto valid = this->getTlmChan_out(0, this->m_pendingHostFunction.u.telemetry.chanId, time, tlmBuffer);
292 
293  // Write the response into guest memory
294  FW_ASSERT(this->m_pendingHostFunction.u.telemetry.timeLen == Fw::Time::SERIALIZED_SIZE,
295  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.telemetry.timeLen), Fw::Time::SERIALIZED_SIZE);
297 
298  auto serStatus = time.serializeTo(timeBuf);
299  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
300 
301  // Write the time
302  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TELEMETRY,
303  this->m_pendingHostFunction.u.telemetry.timePtr, timeBuf.getBuffAddr(),
304  this->m_pendingHostFunction.u.telemetry.timeLen) != Fw::Success::SUCCESS) {
306  return;
307  }
308 
309  if (tlmBuffer.getSize() > this->m_pendingHostFunction.u.telemetry.valueLen) {
311  this->m_pendingHostFunction.u.telemetry.valueLen,
312  static_cast<U32>(tlmBuffer.getSize()));
314  return;
315  }
316 
317  // Write the value
318  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TELEMETRY,
319  this->m_pendingHostFunction.u.telemetry.valuePtr, tlmBuffer.getBuffAddr(),
320  tlmBuffer.getSize()) != Fw::Success::SUCCESS) {
322  return;
323  }
324 
325  this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(valid.e));
326 }
327 
328 void WasmSequencer ::dispatchParameter() {
329  Fw::ParamBuffer prmBuf;
330  auto prmStatus = this->getParam_out(0, this->m_pendingHostFunction.u.parameter.prmId, prmBuf);
331 
332  if (prmBuf.getSize() > this->m_pendingHostFunction.u.parameter.len) {
334  this->m_pendingHostFunction.u.parameter.len,
335  static_cast<U32>(prmBuf.getSize()));
337  return;
338  }
339 
340  // Write the parameter to linear memory
341  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::PARAMETER, this->m_pendingHostFunction.u.parameter.ptr,
342  prmBuf.getBuffAddr(), prmBuf.getSize()) != Fw::Success::SUCCESS) {
344  return;
345  }
346 
347  this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(prmStatus.e));
348 }
349 
350 void WasmSequencer ::dispatchEvent() {
351  U8 stringStorage[FW_LOG_STRING_MAX_SIZE + 1];
352  FW_ASSERT(this->m_pendingHostFunction.u.event.msgLen <= FW_LOG_STRING_MAX_SIZE,
353  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.event.msgLen), FW_LOG_STRING_MAX_SIZE);
354  const Fw::ExternalString msg(reinterpret_cast<char*>(stringStorage), FW_LOG_STRING_MAX_SIZE + 1);
355 
356  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::EVENT, this->m_pendingHostFunction.u.event.msgPtr,
357  stringStorage, this->m_pendingHostFunction.u.event.msgLen) != Fw::Success::SUCCESS) {
359  return;
360  }
361  stringStorage[this->m_pendingHostFunction.u.event.msgLen] = 0;
362 
363  // Emit the event at the guest-requested severity. FATAL and
364  // COMMAND are forbidden for guest programs (FATAL would let
365  // untrusted code trigger the FatalHandler; COMMAND is reserved
366  // for the command dispatcher). A forbidden or out-of-range
367  // severity is reported via HostFunctionInvalidSeverity, carrying
368  // the raw id and the guest message, and the guest continues.
369  const I32 rawSeverity = static_cast<I32>(this->m_pendingHostFunction.u.event.rawSeverity);
370  switch (static_cast<Fw::LogSeverity::T>(rawSeverity)) {
372  this->log_WARNING_HI_LogWarningHi(msg);
373  break;
375  this->log_WARNING_LO_LogWarningLo(msg);
376  break;
379  break;
382  break;
384  this->log_DIAGNOSTIC_LogDiagnostic(msg);
385  break;
386  case Fw::LogSeverity::FATAL: // fallthrough to catch restricted severities
388  default:
389  this->log_WARNING_HI_HostFunctionInvalidSeverity(rawSeverity, msg);
390  break;
391  }
392 
394 }
395 
396 void WasmSequencer ::dispatchRelativeSleep() {
397  const U32 seconds = static_cast<U32>(this->m_pendingHostFunction.u.rsleep.us / 1000000);
398  const U32 useconds = static_cast<U32>(this->m_pendingHostFunction.u.rsleep.us % 1000000);
399 
400  const Fw::Time now = this->getTime();
401  const U64 deadlineSeconds =
402  static_cast<U64>(now.getSeconds()) + seconds + (static_cast<U64>(now.getUSeconds()) + useconds) / 1000000u;
403 
404  Fw::Time timer = now;
405  if (deadlineSeconds > static_cast<U64>(std::numeric_limits<U32>::max())) {
406  timer.set(std::numeric_limits<U32>::max(), 999999u);
407  } else {
408  timer.add(seconds, useconds);
409  }
410 
411  this->m_pendingTimer = timer;
412  this->m_hasPendingTimer = true;
413 }
414 
415 void WasmSequencer ::dispatchAbsoluteSleep() {
416  U32 seconds = static_cast<U32>(this->m_pendingHostFunction.u.asleep.us / 1000000);
417  U32 useconds = static_cast<U32>(this->m_pendingHostFunction.u.asleep.us % 1000000);
418 
419  // Absolute is relative to epoch, we still need to get the time for base/context
420  Fw::Time timer = this->getTime();
421  timer.set(seconds, useconds);
422 
423  this->m_pendingTimer = timer;
424  this->m_hasPendingTimer = true;
425 }
426 
427 void WasmSequencer ::dispatchTime() {
428  auto time = this->getTime();
429 
430  FW_ASSERT(this->m_pendingHostFunction.u.time.len == Fw::Time::SERIALIZED_SIZE,
431  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.time.len), Fw::Time::SERIALIZED_SIZE);
433 
434  auto serStatus = time.serializeTo(timeBuf);
435  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
436 
437  // Write the time
438  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TIME, this->m_pendingHostFunction.u.time.ptr,
439  timeBuf.getBuffAddr(), this->m_pendingHostFunction.u.time.len) != Fw::Success::SUCCESS) {
441  return;
442  }
443 
445 }
446 
447 void WasmSequencer ::dispatchSerialOut() {
448  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialOut.index);
449 
450  // Copy the payload out of guest memory into our own buffer
451  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_OUT, this->m_pendingHostFunction.u.serialOut.ptr,
452  this->m_serialOutBuffer.getBuffAddr(),
453  this->m_pendingHostFunction.u.serialOut.len) != Fw::Success::SUCCESS) {
455  return;
456  }
457 
458  auto serStatus = this->m_serialOutBuffer.setBuffLen(this->m_pendingHostFunction.u.serialOut.len);
459  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
460 
461  // Invoke the serial output port.
462  serStatus = this->serialOut_out(portNum, this->m_serialOutBuffer);
463  if (serStatus != Fw::FW_SERIALIZE_OK) {
465  static_cast<I32>(serStatus));
467  return;
468  }
469 
470  // Send worked, wake up the interpreter
472 }
473 
474 void WasmSequencer ::dispatchSerialRecv() {
475  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialRecv.index);
476  FW_ASSERT(portNum < NUM_SERIALIN_INPUT_PORTS, portNum);
477  Os::ScopeLock scopeLock(this->m_serialInMutex);
478  auto& queue = this->m_serialInQueue[portNum];
479 
480  // Check if there is an available message on the queue
481  if (queue.get_allocated_size() > 0) {
482  // There is data available signal to the state machine to pull this data and wake up without blocking
484  } else {
485  // Check if we should block or not
486  switch (this->m_pendingHostFunction.u.serialRecv.blockingType) {
488  // Do not immediately resume the interpreter, this will let the state machine asynchronously
489  // wait for a signal that we got a message (or timeout).
490  this->m_hostFunctionStart = this->getTime();
491  this->m_hasHostFunctionStart = true;
492  break;
494  // We are non-blocking and the queue is empty, report this back to the interpreter and wake back
495  // up
496  constexpr I32 FPRIME_SERIAL_RECV_QUEUE_STATUS_EMPTY = 1;
497  this->interpreter_sendSignal_hostResumeI32(FPRIME_SERIAL_RECV_QUEUE_STATUS_EMPTY);
498  break;
499  }
500  }
501 }
502 
503 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearPendingHostFunction(
504  SmId smId,
506  this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::NONE;
507  this->m_pendingHostFunction.caller = nullptr;
508  this->m_hasPendingTimer = false;
509  this->m_hasHostFunctionStart = false;
510 }
511 
512 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setContext(
513  SmId smId,
515  const Svc::WasmSequencer_RequestContext& value) {
516  FW_ASSERT(!this->m_hasExecutingContext);
517  this->m_hasExecutingContext = true;
518  this->m_executingContext = value;
519 
520  // A fresh execution window is starting. Bump the sequence counter so any command
521  // dispatched from this program carries a distinct cmdUid
522  this->m_sequencesStarted++;
523 }
524 
525 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearContext(
526  SmId smId,
528  this->m_hasExecutingContext = false;
529 }
530 
531 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_resume(
532  SmId smId,
534  FW_ASSERT(this->m_wasm != nullptr);
535  auto status = spacewasm_resume(this->m_wasm);
536  FW_ASSERT(status == SPACEWASM_OK, status);
537 }
538 
539 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_resumeI32(
540  SmId smId,
542  I32 value) {
543  FW_ASSERT(this->m_wasm != nullptr);
544  spacewasm_value_t return_val;
545  return_val.tag = SPACEWASM_I32;
546  return_val.u.i32_ = value;
547 
548  auto status = spacewasm_resume_value(this->m_wasm, return_val);
549  FW_ASSERT(status == SPACEWASM_OK, status);
550 }
551 
552 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_checkSleepTimers(
553  SmId smId,
555  // Check if we have overrun the timer
556  FW_ASSERT(this->m_hasPendingTimer);
557 
558  const Fw::Time now = this->getTime();
559  switch (Fw::Time::compare(now, this->m_pendingTimer)) {
561  // No timer overrun
562  break;
564  case Fw::TimeComparison::GT: {
565  // Timeout!
567  break;
568  }
570  // Time base / context changed since we set the timer
572  break;
573  }
574 }
575 
576 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_checkTimeout(
577  SmId smId,
579  // Only blocking host functions that await an external event are subject to the
580  // host-function timeout (COMMAND -> cmdResponseIn, blocking SERIAL_RECV -> serialIn).
581  // Sleeps have their own wake timer (checkSleepTimers), separate from this timeout.
582  if (!this->m_hasHostFunctionStart) {
583  return;
584  }
585 
586  Fw::ParamValid prmValid;
587  const F32 timeoutSecs = this->paramGet_HOST_FUNCTION_TIMEOUT_SECS(prmValid);
588 
589  // A non-positive or out-of-range timeout disables the check entirely.
590  if (timeoutSecs <= 0.0f || timeoutSecs >= static_cast<F32>(std::numeric_limits<U32>::max())) {
591  return;
592  }
593 
594  // Deadline = function start + timeout. Round microseconds up so the timeout
595  // is never reported early.
596  U32 seconds = static_cast<U32>(timeoutSecs);
597  U32 useconds = static_cast<U32>((timeoutSecs - static_cast<F32>(seconds)) * 1000000.0f + 0.5f);
598 
599  // Rounding up can push the microsecond field to 1000000; carry it into
600  // seconds so Fw::Time::add() always receives a normalized (< 1e6) value.
601  if (useconds >= 1000000u) {
602  seconds += useconds / 1000000u;
603  useconds %= 1000000u;
604  }
605 
606  Fw::Time deadline = this->m_hostFunctionStart;
607  deadline.add(seconds, useconds);
608 
609  const Fw::Time now = this->getTime();
610  switch (Fw::Time::compare(now, deadline)) {
612  // Deadline not yet reached.
613  break;
616  // Host function timed out waiting for its reply.
618  break;
620  // Time base / context changed since the host function started.
622  break;
623  }
624 }
625 
626 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_dequeueSerialAndResume(
627  SmId smId,
629  const FwIndexType& value) {
630  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialRecv.index);
631  FW_ASSERT(portNum < NUM_SERIALIN_INPUT_PORTS, portNum);
632  Os::ScopeLock scopeLock(this->m_serialInMutex);
633  auto& queue = this->m_serialInQueue[portNum];
634 
635  this->m_dequeueSucceeded = false;
636 
637  // Message is available, pull out the size
638  U32 msgSize;
639  auto status = queue.peek(msgSize);
640  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
641 
642  if (msgSize > this->m_pendingHostFunction.u.serialRecv.dataSize) {
644  this->m_pendingHostFunction.u.serialRecv.dataSize, msgSize);
645  // The frame doesn't fit in the guest's buffer and never will; drop it from the queue
646  // so it doesn't poison every subsequent serial_recv call on this port.
647  status = queue.rotate(static_cast<U32>(sizeof(U32)) + msgSize);
648  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
649  return;
650  }
651 
652  // The queue holds [U32 size][payload]; skip the size prefix when copying the payload.
653  const U32 queuePayloadStart = sizeof(U32);
654  const U32 dataSize = static_cast<U32>(sizeof(U32)) + msgSize;
655 
656  // Write the message size in little endian to the guest memory
658  status = msgSizeSer.serializeFrom(msgSize, Fw::Endianness::LITTLE);
659  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
660 
661  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
662  this->m_pendingHostFunction.u.serialRecv.actualSizePtr, msgSizeSer.getBuffAddr(),
663  sizeof(U32)) != Fw::Success::SUCCESS) {
664  return;
665  }
666 
667  // Pull the message off the queue, we may need to do it in multiple chunks
668  constexpr U32 CHUNK_SIZE = 32;
669  U8 scratch[CHUNK_SIZE];
670 
671  // Pull all the full chunks off the queue
672  U32 queueOffset = queuePayloadStart;
673  for (; (queueOffset + CHUNK_SIZE) <= dataSize; queueOffset += CHUNK_SIZE) {
674  status = queue.peek(scratch, CHUNK_SIZE, queueOffset);
675  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
676 
677  // Copy the data into the guest memory
678  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
679  this->m_pendingHostFunction.u.serialRecv.dataPtr + (queueOffset - queuePayloadStart),
680  scratch, CHUNK_SIZE) != Fw::Success::SUCCESS) {
681  return;
682  }
683  }
684 
685  // Pull out the final partial chunk
686  if (queueOffset < dataSize) {
687  const U32 remaining = dataSize - queueOffset;
688  FW_ASSERT(remaining < CHUNK_SIZE, static_cast<FwAssertArgType>(dataSize),
689  static_cast<FwAssertArgType>(queueOffset), CHUNK_SIZE);
690 
691  status = queue.peek(scratch, remaining, queueOffset);
692  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
693 
694  // Copy the data into the guest memory
695  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
696  this->m_pendingHostFunction.u.serialRecv.dataPtr + (queueOffset - queuePayloadStart),
697  scratch, remaining) != Fw::Success::SUCCESS) {
698  return;
699  }
700  }
701 
702  // Dequeue the message now that we fully copied it into guest memory
703  status = queue.rotate(dataSize);
704  FW_ASSERT(status == Fw::FW_SERIALIZE_OK);
705 
706  this->m_dequeueSucceeded = true;
707 
708  // Yay, we successfully dequeued a message from the queue, wake up the interpreter to tell it about our success
709  constexpr I32 FPRIME_SERIAL_RECV_QUEUE_STATUS_OK = 0;
710  spacewasm_value_t return_val;
711  return_val.tag = SPACEWASM_I32;
712  return_val.u.i32_ = FPRIME_SERIAL_RECV_QUEUE_STATUS_OK;
713 
714  auto resumeStatus = spacewasm_resume_value(this->m_wasm, return_val);
715  FW_ASSERT(resumeStatus == SPACEWASM_OK, resumeStatus);
716 }
717 
718 // ----------------------------------------------------------------------
719 // Implementations for internal state machine guards
720 // ----------------------------------------------------------------------
721 
722 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingPause(
723  SmId smId,
725  return this->m_pendingPause;
726 }
727 
728 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingHostFunction(
729  SmId smId,
731  return this->m_pendingHostFunction.isPending();
732 }
733 
734 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingHostFunctionIsSleep(
735  SmId smId,
737  return this->m_pendingHostFunction.kind == WasmSequencer_HostFunction::ASLEEP ||
738  this->m_pendingHostFunction.kind == WasmSequencer_HostFunction::RSLEEP;
739 }
740 
741 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_blockingSerialIn(
742  SmId smId,
744  const FwIndexType& value) const {
745  return (this->m_pendingHostFunction.kind == Svc::WasmSequencer_HostFunction::SERIAL_RECV &&
746  this->m_pendingHostFunction.u.serialRecv.index == static_cast<U32>(value));
747 }
748 
749 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_dequeueSucceeded(
750  SmId smId,
752  return this->m_dequeueSucceeded;
753 }
754 
755 } // namespace Svc
void log_ACTIVITY_LO_LogActivityLo(const Fw::StringBase &msg) const
union spacewasm_value_payload_t u
Definition: spacewasm.h:412
Serialization/Deserialization operation was successful.
void interpreter_sendSignal_hostResumeI32(I32 value)
Send signal hostResumeI32 to state machine interpreter.
Fw::ParamValid getParam_out(FwIndexType portNum, FwPrmIdType id, Fw::ParamBuffer &val) const
Invoke output port getParam.
U16 FwPacketDescriptorType
The width of packet descriptors when they are serialized by the framework.
spacewasm_run_status_t spacewasm_run(struct spacewasm_t *engine, size_t fuel, spacewasm_trap_t *out_trap)
spacewasm_status_t spacewasm_mem_write(struct spacewasm_caller_t *caller, uint32_t addr, const uint8_t *src, size_t len)
The interpreter exited via HOST_TRAP and we indicated HOST_PANIC in the host function.
Representing success.
PlatformSizeType FwSizeType
void controller_sendSignal_engineFinished(const Svc::WasmSequencer_RequestContext &value)
Send signal engineFinished to state machine controller.
We got an unexpected async reply which caused us to abort.
SerializeStatus moveSerToOffset(FwSizeType offset) override
Move serialization pointer to specified offset.
void log_ACTIVITY_HI_LogActivityHi(const Fw::StringBase &msg) const
Serializable::SizeType getSize() const override
Get current buffer size.
void interpreter_sendSignal_entered()
Send signal entered to state machine interpreter.
void interpreter_sendSignal_serialInMessage(const FwIndexType &value)
Send signal serialInMessage to state machine interpreter.
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
A cancel signal was sent and we exited the interpreter.
SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG) override
Serialize an 8-bit unsigned integer value.
spacewasm_run_status_t
Definition: spacewasm.h:244
FwSizeType paramGet_INSTRUCTION_FUEL(Fw::ParamValid &valid)
void log_WARNING_HI_HostFunctionInvalidPointer(const Svc::WasmSequencer_HostFunction &host_function, const Svc::WasmSequencer_Status &code) const
Log event HostFunctionInvalidPointer.
Software diagnostic events.
An async host function replied with an error.
An async reply timeout overran while waiting for a host function reply.
spacewasm_status_t spacewasm_mem_read(struct spacewasm_caller_t *caller, uint32_t addr, uint8_t *dst, size_t len)
float F32
32-bit floating point
Definition: BasicTypes.h:84
spacewasm_trap_t
Definition: spacewasm.h:266
void cmdOut_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context) const
Invoke output port cmdOut.
void set(U32 seconds, U32 useconds)
Definition: Time.cpp:24
void interpreter_sendSignal_interpreterHostFunctionNeedsPause()
Send signal interpreterHostFunctionNeedsPause to state machine interpreter.
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
Less important informational events.
An activity related to commanding.
A less serious but recoverable event.
void interpreter_sendSignal_hostResponseFailure()
Send signal hostResponseFailure to state machine interpreter.
U32 getSeconds() const
Definition: Time.cpp:128
spacewasm_status_t spacewasm_get_result(struct spacewasm_t *engine, spacewasm_valtype_t expected, struct spacewasm_value_t *out)
void log_WARNING_HI_LogWarningHi(const Fw::StringBase &msg) const
void interpreter_sendSignal_interpreterFinished(I32 value)
Send signal interpreterFinished to state machine interpreter.
spacewasm_status_t spacewasm_resume_value(struct spacewasm_t *engine, struct spacewasm_value_t resume_value)
spacewasm_valtype_t tag
Definition: spacewasm.h:411
void interpreter_sendSignal_hostResume()
Send signal hostResume to state machine interpreter.
A string backed by an external buffer.
A serious but recoverable event.
void log_WARNING_HI_HostFunctionInvalidSeverity(I32 raw, const Fw::StringBase &msg) const
The interpreter exited via HOST_TRAP but a host function indicated it was a standard exit() ...
void interpreter_sendSignal_hostResponseTimeout()
Send signal hostResponseTimeout to state machine interpreter.
Representing failure.
Fw::SerializeStatus serialOut_out(FwIndexType portNum, Fw::LinearBufferBase &buffer)
Invoke output port serialOut.
Command successfully executed.
Fw::TlmValid getTlmChan_out(FwIndexType portNum, FwChanIdType id, Fw::Time &timeTag, Fw::TlmBuffer &val) const
Invoke output port getTlmChan.
F32 paramGet_HOST_FUNCTION_TIMEOUT_SECS(Fw::ParamValid &valid)
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
void log_WARNING_LO_LogWarningLo(const Fw::StringBase &msg) const
Important informational events.
static TimeComparison compare(const Time &time1, const Time &time2)
Definition: Time.cpp:149
A sleep or host-function timer became incomparable (time base changed) and could not be evaluated...
static Time add(const Time &a, const Time &b)
Definition: Time.cpp:174
void interpreter_sendSignal_interpreterTrap(const Svc::WasmSequencer_TrapReason &value)
Send signal interpreterTrap to state machine interpreter.
spacewasm_status_t spacewasm_reset(struct spacewasm_t *engine)
spacewasm_status_t
Definition: spacewasm.h:37
U32 getUSeconds() const
Definition: Time.cpp:132
void interpreter_sendSignal_hostResponseTimeIncomparable()
Send signal hostResponseTimeIncomparable to state machine interpreter.
PlatformIndexType FwIndexType
locks a mutex within the current scope
The interpreter exited normally and set m_exitCode.
A fatal non-recoverable event.
void interpreter_sendSignal_interpreterOutOfFuel()
Send signal interpreterOutOfFuel to state machine interpreter.
RateGroupDivider component implementation.
Enum representing parameter validity.
Little endian serialization.
void log_DIAGNOSTIC_LogDiagnostic(const Fw::StringBase &msg) const
SerializeStatus serializeTo(SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) const override
Serialize the contents of this object to a buffer.
Definition: Time.cpp:110
A struct holding the context needed to reply to a command request.
FpySequencer_SequencerStateMachineStateMachineBase::Signal Signal
#define FW_ASSERT(...)
Definition: Assert.hpp:14
spacewasm_status_t spacewasm_resume(struct spacewasm_t *engine)
Success/Failure.
void log_WARNING_HI_SerialPortSendFailed(const Svc::WasmSequencer_HostFunction &host_function, I32 status) const
We did not explicitely set the exit reason, bug?
void log_WARNING_HI_BufferTooSmall(const Svc::WasmSequencer_HostFunction &host_function, U32 size, U32 valueSize) const