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->dispatchArgs();
218  break;
220  this->dispatchTime();
221  break;
223  this->dispatchSerialOut();
224  break;
226  this->dispatchSerialRecv();
227  break;
228  }
229 }
230 
231 Fw::Success WasmSequencer ::readGuestMemory(WasmSequencer_HostFunction::T kind, U32 addr, U8* dst, FwSizeType len) {
232  const spacewasm_status_t status = spacewasm_mem_read(this->m_pendingHostFunction.caller, addr, dst, len);
233  if (status != SPACEWASM_OK) {
234  this->log_WARNING_HI_HostFunctionInvalidPointer(kind, static_cast<WasmSequencer_Status::T>(status));
235  return Fw::Success::FAILURE;
236  } else {
237  return Fw::Success::SUCCESS;
238  }
239 }
240 
241 Fw::Success WasmSequencer ::writeGuestMemory(WasmSequencer_HostFunction::T kind,
242  U32 addr,
243  const U8* src,
244  FwSizeType len) {
245  const spacewasm_status_t status = spacewasm_mem_write(this->m_pendingHostFunction.caller, addr, src, len);
246  if (status != SPACEWASM_OK) {
247  this->log_WARNING_HI_HostFunctionInvalidPointer(kind, static_cast<WasmSequencer_Status::T>(status));
248  return Fw::Success::FAILURE;
249  } else {
250  return Fw::Success::SUCCESS;
251  }
252 }
253 
254 // ----------------------------------------------------------------------
255 // Per-host-function dispatch helpers (arms of dispatchPendingHostFunction)
256 // ----------------------------------------------------------------------
257 
258 void WasmSequencer ::dispatchCommand() {
259  Fw::ComBuffer cmd;
260 
261  // Write the CMD descriptor to the ComBuffer
262  auto serStatus = cmd.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
263  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
264 
265  // Copy the com buffer from the guest memory into our memory
266  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::COMMAND, this->m_pendingHostFunction.u.command.ptr,
267  cmd.getBuffAddr() + sizeof(FwPacketDescriptorType),
268  this->m_pendingHostFunction.u.command.len) != Fw::Success::SUCCESS) {
270  return;
271  }
272 
273  // Memory read succeeded, update the ComBuffer to hold the encoded command
274  serStatus = cmd.moveSerToOffset(this->m_pendingHostFunction.u.command.len + sizeof(FwPacketDescriptorType));
275  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
276 
277  // Dispatch command to CmdDisp. The command context (cmdUid)
278  // encodes the current sequence + command instance so we can
279  // reject late/stale responses in cmdResponseIn_handler.
280  this->m_tlm.commandsDispatched++;
281 
282  // Start the host-function timeout clock: we are about to block in
283  // AWAITING_RESPONSE until the command response comes back in.
284  this->m_hostFunctionStart = this->getTime();
285  this->m_hasHostFunctionStart = true;
286 
287  this->cmdOut_out(0, cmd, this->makeCmdUid());
288 }
289 
290 void WasmSequencer ::dispatchTelemetry() {
291  Fw::Time time;
292  Fw::TlmBuffer tlmBuffer;
293 
294  auto valid = this->getTlmChan_out(0, this->m_pendingHostFunction.u.telemetry.chanId, time, tlmBuffer);
295 
296  // Write the response into guest memory
297  FW_ASSERT(this->m_pendingHostFunction.u.telemetry.timeLen == Fw::Time::SERIALIZED_SIZE,
298  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.telemetry.timeLen), Fw::Time::SERIALIZED_SIZE);
300 
301  auto serStatus = time.serializeTo(timeBuf);
302  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
303 
304  // Write the time
305  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TELEMETRY,
306  this->m_pendingHostFunction.u.telemetry.timePtr, timeBuf.getBuffAddr(),
307  this->m_pendingHostFunction.u.telemetry.timeLen) != Fw::Success::SUCCESS) {
309  return;
310  }
311 
312  if (tlmBuffer.getSize() > this->m_pendingHostFunction.u.telemetry.valueLen) {
314  this->m_pendingHostFunction.u.telemetry.valueLen,
315  static_cast<U32>(tlmBuffer.getSize()));
317  return;
318  }
319 
320  // Write the value
321  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TELEMETRY,
322  this->m_pendingHostFunction.u.telemetry.valuePtr, tlmBuffer.getBuffAddr(),
323  tlmBuffer.getSize()) != Fw::Success::SUCCESS) {
325  return;
326  }
327 
328  this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(valid.e));
329 }
330 
331 void WasmSequencer ::dispatchParameter() {
332  Fw::ParamBuffer prmBuf;
333  auto prmStatus = this->getParam_out(0, this->m_pendingHostFunction.u.parameter.prmId, prmBuf);
334 
335  if (prmBuf.getSize() > this->m_pendingHostFunction.u.parameter.len) {
337  this->m_pendingHostFunction.u.parameter.len,
338  static_cast<U32>(prmBuf.getSize()));
340  return;
341  }
342 
343  // Write the parameter to linear memory
344  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::PARAMETER, this->m_pendingHostFunction.u.parameter.ptr,
345  prmBuf.getBuffAddr(), prmBuf.getSize()) != Fw::Success::SUCCESS) {
347  return;
348  }
349 
350  this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(prmStatus.e));
351 }
352 
353 void WasmSequencer ::dispatchEvent() {
354  U8 stringStorage[FW_LOG_STRING_MAX_SIZE + 1];
355  FW_ASSERT(this->m_pendingHostFunction.u.event.msgLen <= FW_LOG_STRING_MAX_SIZE,
356  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.event.msgLen), FW_LOG_STRING_MAX_SIZE);
357  const Fw::ExternalString msg(reinterpret_cast<char*>(stringStorage), FW_LOG_STRING_MAX_SIZE + 1);
358 
359  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::EVENT, this->m_pendingHostFunction.u.event.msgPtr,
360  stringStorage, this->m_pendingHostFunction.u.event.msgLen) != Fw::Success::SUCCESS) {
362  return;
363  }
364  stringStorage[this->m_pendingHostFunction.u.event.msgLen] = 0;
365 
366  // Emit the event at the guest-requested severity. FATAL and
367  // COMMAND are forbidden for guest programs (FATAL would let
368  // untrusted code trigger the FatalHandler; COMMAND is reserved
369  // for the command dispatcher). A forbidden or out-of-range
370  // severity is reported via HostFunctionInvalidSeverity, carrying
371  // the raw id and the guest message, and the guest continues.
372  const I32 rawSeverity = static_cast<I32>(this->m_pendingHostFunction.u.event.rawSeverity);
373  switch (static_cast<Fw::LogSeverity::T>(rawSeverity)) {
375  this->log_WARNING_HI_LogWarningHi(msg);
376  break;
378  this->log_WARNING_LO_LogWarningLo(msg);
379  break;
382  break;
385  break;
387  this->log_DIAGNOSTIC_LogDiagnostic(msg);
388  break;
389  case Fw::LogSeverity::FATAL: // fallthrough to catch restricted severities
391  default:
392  this->log_WARNING_HI_HostFunctionInvalidSeverity(rawSeverity, msg);
393  break;
394  }
395 
397 }
398 
399 void WasmSequencer ::dispatchRelativeSleep() {
400  const U32 seconds = static_cast<U32>(this->m_pendingHostFunction.u.rsleep.us / 1000000);
401  const U32 useconds = static_cast<U32>(this->m_pendingHostFunction.u.rsleep.us % 1000000);
402 
403  const Fw::Time now = this->getTime();
404  const U64 deadlineSeconds =
405  static_cast<U64>(now.getSeconds()) + seconds + (static_cast<U64>(now.getUSeconds()) + useconds) / 1000000u;
406 
407  Fw::Time timer = now;
408  if (deadlineSeconds > static_cast<U64>(std::numeric_limits<U32>::max())) {
409  timer.set(std::numeric_limits<U32>::max(), 999999u);
410  } else {
411  timer.add(seconds, useconds);
412  }
413 
414  this->m_pendingTimer = timer;
415  this->m_hasPendingTimer = true;
416 }
417 
418 void WasmSequencer ::dispatchAbsoluteSleep() {
419  U32 seconds = static_cast<U32>(this->m_pendingHostFunction.u.asleep.us / 1000000);
420  U32 useconds = static_cast<U32>(this->m_pendingHostFunction.u.asleep.us % 1000000);
421 
422  // Absolute is relative to epoch, we still need to get the time for base/context
423  Fw::Time timer = this->getTime();
424  timer.set(seconds, useconds);
425 
426  this->m_pendingTimer = timer;
427  this->m_hasPendingTimer = true;
428 }
429 
430 void WasmSequencer ::dispatchArgs() {
431  const FwSizeType argCapacity = static_cast<FwSizeType>(sizeof(this->m_args.get_buffer()));
432  if (this->m_args.get_size() > argCapacity) {
433  this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::ARGS, static_cast<U32>(this->m_args.get_size()),
434  static_cast<U32>(argCapacity));
436  return;
437  }
438 
439  if (this->m_args.get_size() > this->m_pendingHostFunction.u.args.len) {
440  // Too many param bytes and we are going to leak data into the guest memory
441  this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::ARGS, this->m_pendingHostFunction.u.args.len,
442  static_cast<U32>(this->m_args.get_size()));
444  return;
445  }
446 
447  // Write the arguments to linear memory
448  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::ARGS, this->m_pendingHostFunction.u.args.ptr,
449  this->m_args.get_buffer(), this->m_args.get_size()) != Fw::Success::SUCCESS) {
451  return;
452  }
453 
454  this->interpreter_sendSignal_hostResumeI32(static_cast<I32>(this->m_args.get_size()));
455 }
456 
457 void WasmSequencer ::dispatchTime() {
458  auto time = this->getTime();
459 
460  FW_ASSERT(this->m_pendingHostFunction.u.time.len == Fw::Time::SERIALIZED_SIZE,
461  static_cast<FwAssertArgType>(this->m_pendingHostFunction.u.time.len), Fw::Time::SERIALIZED_SIZE);
463 
464  auto serStatus = time.serializeTo(timeBuf);
465  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
466 
467  // Write the time
468  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::TIME, this->m_pendingHostFunction.u.time.ptr,
469  timeBuf.getBuffAddr(), this->m_pendingHostFunction.u.time.len) != Fw::Success::SUCCESS) {
471  return;
472  }
473 
475 }
476 
477 void WasmSequencer ::dispatchSerialOut() {
478  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialOut.index);
479 
480  // Copy the payload out of guest memory into our own buffer
481  if (this->readGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_OUT, this->m_pendingHostFunction.u.serialOut.ptr,
482  this->m_serialOutBuffer.getBuffAddr(),
483  this->m_pendingHostFunction.u.serialOut.len) != Fw::Success::SUCCESS) {
485  return;
486  }
487 
488  auto serStatus = this->m_serialOutBuffer.setBuffLen(this->m_pendingHostFunction.u.serialOut.len);
489  FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
490 
491  // Invoke the serial output port.
492  serStatus = this->serialOut_out(portNum, this->m_serialOutBuffer);
493  if (serStatus != Fw::FW_SERIALIZE_OK) {
495  static_cast<I32>(serStatus));
497  return;
498  }
499 
500  // Send worked, wake up the interpreter
502 }
503 
504 void WasmSequencer ::dispatchSerialRecv() {
505  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialRecv.index);
506  FW_ASSERT(portNum < NUM_SERIALIN_INPUT_PORTS, portNum);
507  Os::ScopeLock scopeLock(this->m_serialInMutex);
508  auto& queue = this->m_serialInQueue[portNum];
509 
510  // Check if there is an available message on the queue
511  if (queue.get_allocated_size() > 0) {
512  // There is data available signal to the state machine to pull this data and wake up without blocking
514  } else {
515  // Check if we should block or not
516  switch (this->m_pendingHostFunction.u.serialRecv.blockingType) {
518  // Do not immediately resume the interpreter, this will let the state machine asynchronously
519  // wait for a signal that we got a message (or timeout).
520  this->m_hostFunctionStart = this->getTime();
521  this->m_hasHostFunctionStart = true;
522  break;
524  // We are non-blocking and the queue is empty, report this back to the interpreter and wake back
525  // up
526  constexpr I32 FPRIME_SERIAL_RECV_QUEUE_STATUS_EMPTY = 1;
527  this->interpreter_sendSignal_hostResumeI32(FPRIME_SERIAL_RECV_QUEUE_STATUS_EMPTY);
528  break;
529  }
530  }
531 }
532 
533 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearPendingHostFunction(
534  SmId smId,
536  this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::NONE;
537  this->m_pendingHostFunction.caller = nullptr;
538  this->m_hasPendingTimer = false;
539  this->m_hasHostFunctionStart = false;
540 }
541 
542 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_setContext(
543  SmId smId,
545  const Svc::WasmSequencer_RequestContext& value) {
546  FW_ASSERT(!this->m_hasExecutingContext);
547  this->m_hasExecutingContext = true;
548  this->m_executingContext = value;
549 
550  // A fresh execution window is starting. Bump the sequence counter so any command
551  // dispatched from this program carries a distinct cmdUid
552  this->m_sequencesStarted++;
553 }
554 
555 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_clearContext(
556  SmId smId,
558  this->m_hasExecutingContext = false;
559 }
560 
561 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_resume(
562  SmId smId,
564  FW_ASSERT(this->m_wasm != nullptr);
565  auto status = spacewasm_resume(this->m_wasm);
566  FW_ASSERT(status == SPACEWASM_OK, status);
567 }
568 
569 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_resumeI32(
570  SmId smId,
572  I32 value) {
573  FW_ASSERT(this->m_wasm != nullptr);
574  spacewasm_value_t return_val;
575  return_val.tag = SPACEWASM_I32;
576  return_val.u.i32_ = value;
577 
578  auto status = spacewasm_resume_value(this->m_wasm, return_val);
579  FW_ASSERT(status == SPACEWASM_OK, status);
580 }
581 
582 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_checkSleepTimers(
583  SmId smId,
585  // Check if we have overrun the timer
586  FW_ASSERT(this->m_hasPendingTimer);
587 
588  const Fw::Time now = this->getTime();
589  switch (Fw::Time::compare(now, this->m_pendingTimer)) {
591  // No timer overrun
592  break;
594  case Fw::TimeComparison::GT: {
595  // Timeout!
597  break;
598  }
600  // Time base / context changed since we set the timer
602  break;
603  }
604 }
605 
606 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_checkTimeout(
607  SmId smId,
609  // Only blocking host functions that await an external event are subject to the
610  // host-function timeout (COMMAND -> cmdResponseIn, blocking SERIAL_RECV -> serialIn).
611  // Sleeps have their own wake timer (checkSleepTimers), separate from this timeout.
612  if (!this->m_hasHostFunctionStart) {
613  return;
614  }
615 
616  Fw::ParamValid prmValid;
617  const F32 timeoutSecs = this->paramGet_HOST_FUNCTION_TIMEOUT_SECS(prmValid);
618 
619  // A non-positive or out-of-range timeout disables the check entirely.
620  if (timeoutSecs <= 0.0f || timeoutSecs >= static_cast<F32>(std::numeric_limits<U32>::max())) {
621  return;
622  }
623 
624  // Deadline = function start + timeout. Round microseconds up so the timeout
625  // is never reported early.
626  U32 seconds = static_cast<U32>(timeoutSecs);
627  U32 useconds = static_cast<U32>((timeoutSecs - static_cast<F32>(seconds)) * 1000000.0f + 0.5f);
628 
629  // Rounding up can push the microsecond field to 1000000; carry it into
630  // seconds so Fw::Time::add() always receives a normalized (< 1e6) value.
631  if (useconds >= 1000000u) {
632  seconds += useconds / 1000000u;
633  useconds %= 1000000u;
634  }
635 
636  Fw::Time deadline = this->m_hostFunctionStart;
637  deadline.add(seconds, useconds);
638 
639  const Fw::Time now = this->getTime();
640  switch (Fw::Time::compare(now, deadline)) {
642  // Deadline not yet reached.
643  break;
646  // Host function timed out waiting for its reply.
648  break;
650  // Time base / context changed since the host function started.
652  break;
653  }
654 }
655 
656 void WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_action_dequeueSerialAndResume(
657  SmId smId,
659  const FwIndexType& value) {
660  const FwIndexType portNum = static_cast<FwIndexType>(this->m_pendingHostFunction.u.serialRecv.index);
661  FW_ASSERT(portNum < NUM_SERIALIN_INPUT_PORTS, portNum);
662  Os::ScopeLock scopeLock(this->m_serialInMutex);
663  auto& queue = this->m_serialInQueue[portNum];
664 
665  this->m_dequeueSucceeded = false;
666 
667  // Message is available, pull out the size
668  U32 msgSize;
669  auto status = queue.peek(msgSize);
670  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
671 
672  if (msgSize > this->m_pendingHostFunction.u.serialRecv.dataSize) {
674  this->m_pendingHostFunction.u.serialRecv.dataSize, msgSize);
675  // The frame doesn't fit in the guest's buffer and never will; drop it from the queue
676  // so it doesn't poison every subsequent serial_recv call on this port.
677  status = queue.rotate(static_cast<U32>(sizeof(U32)) + msgSize);
678  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
679  return;
680  }
681 
682  // The queue holds [U32 size][payload]; skip the size prefix when copying the payload.
683  const U32 queuePayloadStart = sizeof(U32);
684  const U32 dataSize = static_cast<U32>(sizeof(U32)) + msgSize;
685 
686  // Write the message size in little endian to the guest memory
688  status = msgSizeSer.serializeFrom(msgSize, Fw::Endianness::LITTLE);
689  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
690 
691  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
692  this->m_pendingHostFunction.u.serialRecv.actualSizePtr, msgSizeSer.getBuffAddr(),
693  sizeof(U32)) != Fw::Success::SUCCESS) {
694  return;
695  }
696 
697  // Pull the message off the queue, we may need to do it in multiple chunks
698  constexpr U32 CHUNK_SIZE = 32;
699  U8 scratch[CHUNK_SIZE];
700 
701  // Pull all the full chunks off the queue
702  U32 queueOffset = queuePayloadStart;
703  for (; (queueOffset + CHUNK_SIZE) <= dataSize; queueOffset += CHUNK_SIZE) {
704  status = queue.peek(scratch, CHUNK_SIZE, queueOffset);
705  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
706 
707  // Copy the data into the guest memory
708  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
709  this->m_pendingHostFunction.u.serialRecv.dataPtr + (queueOffset - queuePayloadStart),
710  scratch, CHUNK_SIZE) != Fw::Success::SUCCESS) {
711  return;
712  }
713  }
714 
715  // Pull out the final partial chunk
716  if (queueOffset < dataSize) {
717  const U32 remaining = dataSize - queueOffset;
718  FW_ASSERT(remaining < CHUNK_SIZE, static_cast<FwAssertArgType>(dataSize),
719  static_cast<FwAssertArgType>(queueOffset), CHUNK_SIZE);
720 
721  status = queue.peek(scratch, remaining, queueOffset);
722  FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
723 
724  // Copy the data into the guest memory
725  if (this->writeGuestMemory(Svc::WasmSequencer_HostFunction::SERIAL_RECV,
726  this->m_pendingHostFunction.u.serialRecv.dataPtr + (queueOffset - queuePayloadStart),
727  scratch, remaining) != Fw::Success::SUCCESS) {
728  return;
729  }
730  }
731 
732  // Dequeue the message now that we fully copied it into guest memory
733  status = queue.rotate(dataSize);
734  FW_ASSERT(status == Fw::FW_SERIALIZE_OK);
735 
736  this->m_dequeueSucceeded = true;
737 
738  // Yay, we successfully dequeued a message from the queue, wake up the interpreter to tell it about our success
739  constexpr I32 FPRIME_SERIAL_RECV_QUEUE_STATUS_OK = 0;
740  spacewasm_value_t return_val;
741  return_val.tag = SPACEWASM_I32;
742  return_val.u.i32_ = FPRIME_SERIAL_RECV_QUEUE_STATUS_OK;
743 
744  auto resumeStatus = spacewasm_resume_value(this->m_wasm, return_val);
745  FW_ASSERT(resumeStatus == SPACEWASM_OK, resumeStatus);
746 }
747 
748 // ----------------------------------------------------------------------
749 // Implementations for internal state machine guards
750 // ----------------------------------------------------------------------
751 
752 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingPause(
753  SmId smId,
755  return this->m_pendingPause;
756 }
757 
758 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingHostFunction(
759  SmId smId,
761  return this->m_pendingHostFunction.isPending();
762 }
763 
764 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_pendingHostFunctionIsSleep(
765  SmId smId,
767  return this->m_pendingHostFunction.kind == WasmSequencer_HostFunction::ASLEEP ||
768  this->m_pendingHostFunction.kind == WasmSequencer_HostFunction::RSLEEP;
769 }
770 
771 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_blockingSerialIn(
772  SmId smId,
774  const FwIndexType& value) const {
775  return (this->m_pendingHostFunction.kind == Svc::WasmSequencer_HostFunction::SERIAL_RECV &&
776  this->m_pendingHostFunction.u.serialRecv.index == static_cast<U32>(value));
777 }
778 
779 bool WasmSequencer ::Svc_WasmSequencer_InterpreterStateMachine_guard_dequeueSucceeded(
780  SmId smId,
782  return this->m_dequeueSucceeded;
783 }
784 
785 } // 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.
Type_of_buffer & get_buffer()
Get member buffer.
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
FwSizeType get_size() const
Get member size.
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
void log_WARNING_HI_BufferTooLarge(const Svc::WasmSequencer_HostFunction &host_function, U32 size, U32 maxSize) 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
Definition: Mutex.hpp:80
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