| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title WasmSequencerController.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief cpp file for WasmSequencer controller state machine | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #include "Fw/Cmd/CmdResponseEnumAc.hpp" | ||
| 8 | #include "Fw/Types/Assert.hpp" | ||
| 9 | #include "Os/File.hpp" | ||
| 10 | #include "Svc/Seq/BlockStateEnumAc.hpp" | ||
| 11 | #include "Svc/WasmSequencer/WasmSequencer.hpp" | ||
| 12 | #include "Svc/WasmSequencer/WasmSequencer_LoadRequestSerializableAc.hpp" | ||
| 13 | #include "Svc/WasmSequencer/WasmSequencer_ModuleIdxAliasAc.hpp" | ||
| 14 | #include "Svc/WasmSequencer/WasmSequencer_SignalSourceEnumAc.hpp" | ||
| 15 | #include "config/FwAssertArgTypeAliasAc.h" | ||
| 16 | #include "spacewasm.h" | ||
| 17 | |||
| 18 | namespace Svc { | ||
| 19 | |||
| 20 | // ---------------------------------------------------------------------- | ||
| 21 | // Implementations for internal state machine actions | ||
| 22 | // ---------------------------------------------------------------------- | ||
| 23 | |||
| 24 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_processInvoke( | |
| 25 | SmId smId, | ||
| 26 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 27 | const Svc::WasmSequencer_InvokeRequest& value) { | ||
| 28 | // Resolve the module name to its index within the engine. | ||
| 29 | ✗ | FW_ASSERT(this->m_wasm != nullptr); | |
| 30 | ✗ | U32 moduleIdx = 0; | |
| 31 | const spacewasm_status_t findStatus = | ||
| 32 | ✗ | spacewasm_find_module(this->m_wasm, value.get_moduleName().toChar(), &moduleIdx); | |
| 33 | |||
| 34 | ✗ | if (findStatus != SPACEWASM_OK) { | |
| 35 | ✗ | this->log_WARNING_LO_ModuleNotFound(value.get_moduleName()); | |
| 36 | ✗ | this->controller_sendSignal_invokeFailed(value.get_context()); | |
| 37 | } else { | ||
| 38 | ✗ | Svc::WasmSequencer_RequestContext context = value.get_context(); | |
| 39 | ✗ | context.set_moduleIdx(static_cast<WasmSequencer_ModuleIdx>(moduleIdx)); | |
| 40 | ✗ | this->controller_sendSignal_invoked(context); | |
| 41 | ✗ | } | |
| 42 | ✗ | } | |
| 43 | |||
| 44 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respond_noblock_OK( | |
| 45 | SmId smId, | ||
| 46 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 47 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 48 | // Respond only to NO_BLOCK requests | ||
| 49 | ✗ | if (value.get_block() == Svc::BlockState::NO_BLOCK) { | |
| 50 | // Respond to this request! | ||
| 51 | ✗ | this->respondToRequest(value, Fw::CmdResponse::OK); | |
| 52 | } | ||
| 53 | ✗ | } | |
| 54 | |||
| 55 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respond_ERROR( | |
| 56 | SmId smId, | ||
| 57 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 58 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 59 | ✗ | this->respondToRequest(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 60 | |||
| 61 | // Respond to all wait requests | ||
| 62 | ✗ | this->respondToWaiting(Fw::CmdResponse::EXECUTION_ERROR); | |
| 63 | |||
| 64 | // Respond to port requests | ||
| 65 | ✗ | this->reportSeqAborted(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 66 | ✗ | } | |
| 67 | |||
| 68 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_incrementSequenceFailure( | |
| 69 | SmId smId, | ||
| 70 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) { | ||
| 71 | ✗ | this->m_tlm.sequencesFailed++; | |
| 72 | ✗ | } | |
| 73 | |||
| 74 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_setCancelRequested( | |
| 75 | SmId smId, | ||
| 76 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) { | ||
| 77 | ✗ | this->m_cancelRequested = true; | |
| 78 | ✗ | } | |
| 79 | |||
| 80 | 1 | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_clearCancelRequested( | |
| 81 | SmId smId, | ||
| 82 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) { | ||
| 83 | 1 | this->m_cancelRequested = false; | |
| 84 | 1 | } | |
| 85 | |||
| 86 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_cancelPendingRequest( | |
| 87 | SmId smId, | ||
| 88 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 89 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 90 | ✗ | this->m_tlm.sequencesCancelled++; | |
| 91 | ✗ | this->respondToRequest(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 92 | ✗ | this->respondToWaiting(Fw::CmdResponse::EXECUTION_ERROR); | |
| 93 | |||
| 94 | // Respond to port requests | ||
| 95 | ✗ | this->reportSeqAborted(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 96 | ✗ | } | |
| 97 | |||
| 98 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respondInvoke_BUSY( | |
| 99 | SmId smId, | ||
| 100 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 101 | const Svc::WasmSequencer_InvokeRequest& value) { | ||
| 102 | ✗ | this->log_WARNING_LO_ControllerBusy(value.get_context().get_source(), this->controller_getState()); | |
| 103 | ✗ | this->respondToRequest(value.get_context(), Fw::CmdResponse::BUSY); | |
| 104 | ✗ | } | |
| 105 | |||
| 106 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respondInvoke_ERROR( | |
| 107 | SmId smId, | ||
| 108 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 109 | const Svc::WasmSequencer_InvokeRequest& value) { | ||
| 110 | ✗ | this->log_WARNING_LO_ControllerCannotInvoke(); | |
| 111 | ✗ | this->respondToRequest(value.get_context(), Fw::CmdResponse::EXECUTION_ERROR); | |
| 112 | ✗ | } | |
| 113 | |||
| 114 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respondLoad_BUSY( | |
| 115 | SmId smId, | ||
| 116 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 117 | const Svc::WasmSequencer_LoadRequest& value) { | ||
| 118 | ✗ | this->log_WARNING_LO_ControllerBusy(value.get_context().get_source(), this->controller_getState()); | |
| 119 | ✗ | this->respondToRequest(value.get_context(), Fw::CmdResponse::BUSY); | |
| 120 | |||
| 121 | // Respond to port requests | ||
| 122 | ✗ | this->reportSeqAborted(value.get_context(), Fw::CmdResponse::BUSY); | |
| 123 | ✗ | } | |
| 124 | |||
| 125 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respond_block_OK( | |
| 126 | SmId smId, | ||
| 127 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 128 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 129 | // Respond only to BLOCK requests | ||
| 130 | ✗ | if (value.get_block() == Svc::BlockState::BLOCK) { | |
| 131 | // Respond to this request! | ||
| 132 | ✗ | this->respondToRequest(value, Fw::CmdResponse::OK); | |
| 133 | } | ||
| 134 | |||
| 135 | // Respond to all wait requests | ||
| 136 | ✗ | this->respondToWaiting(Fw::CmdResponse::OK); | |
| 137 | |||
| 138 | // If this concluded an active RUN, report the done to internal callers. | ||
| 139 | ✗ | this->reportSeqDone(value, Fw::CmdResponse::OK); | |
| 140 | ✗ | } | |
| 141 | |||
| 142 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_respond_block_ERROR( | |
| 143 | SmId smId, | ||
| 144 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 145 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 146 | // Respond only to BLOCK requests | ||
| 147 | ✗ | if (value.get_block() == Svc::BlockState::BLOCK) { | |
| 148 | // Respond to this request! | ||
| 149 | ✗ | this->respondToRequest(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 150 | } | ||
| 151 | |||
| 152 | // Respond to all wait requests | ||
| 153 | ✗ | this->respondToWaiting(Fw::CmdResponse::EXECUTION_ERROR); | |
| 154 | |||
| 155 | // If this concluded an active RUN, report the done to internal callers. | ||
| 156 | ✗ | this->reportSeqDone(value, Fw::CmdResponse::EXECUTION_ERROR); | |
| 157 | ✗ | } | |
| 158 | |||
| 159 | ✗ | bool WasmSequencer ::resolveSequencePath(const Fw::StringBase& fileName, Fw::String& filePath) { | |
| 160 | // Resolve the requested path against the SEQ_BASE_DIR parameter | ||
| 161 | ✗ | Fw::ParamValid baseDirValid; | |
| 162 | ✗ | const Fw::ParamString baseDir = this->paramGet_SEQ_BASE_DIR(baseDirValid); | |
| 163 | |||
| 164 | ✗ | if (baseDir.length() == 0) { | |
| 165 | ✗ | filePath = fileName; | |
| 166 | ✗ | return true; | |
| 167 | } | ||
| 168 | |||
| 169 | // With a base dir configured, SEQ_BASE_DIR acts as a containment boundary. | ||
| 170 | // A ground-supplied file name containing a ".." component could escape it | ||
| 171 | // (e.g. "../../etc/passwd"), so reject such names rather than opening a path | ||
| 172 | // outside the configured base directory. | ||
| 173 | ✗ | if (WasmSequencer::pathHasParentTraversal(fileName)) { | |
| 174 | ✗ | this->log_WARNING_HI_SequenceFilePathNotContained(baseDir, fileName); | |
| 175 | ✗ | return false; | |
| 176 | } | ||
| 177 | |||
| 178 | // Join the base dir and file name with exactly one '/' separator. Without an | ||
| 179 | // explicit separator a base dir that lacks a trailing slash both mis-resolves | ||
| 180 | // ordinary names ("seqs" + "a.wasm" -> "seqsa.wasm") and can escape the | ||
| 181 | // containment boundary ("seqs" + "_priv/x.wasm" -> "seqs_priv/x.wasm", a sibling | ||
| 182 | // directory). The ".." rejection above plus a guaranteed base-dir prefix keeps | ||
| 183 | // the resolved path inside the configured directory. | ||
| 184 | ✗ | const FwSizeType baseLen = static_cast<FwSizeType>(baseDir.length()); | |
| 185 | ✗ | const char* const separator = (baseLen > 0 && baseDir.toChar()[baseLen - 1] == '/') ? "" : "/"; | |
| 186 | |||
| 187 | ✗ | const Fw::FormatStatus fmtStatus = filePath.format("%s%s%s", baseDir.toChar(), separator, fileName.toChar()); | |
| 188 | ✗ | if (fmtStatus != Fw::FormatStatus::SUCCESS) { | |
| 189 | ✗ | FW_ASSERT(fmtStatus == Fw::FormatStatus::OVERFLOWED, static_cast<FwAssertArgType>(fmtStatus)); | |
| 190 | ✗ | this->log_WARNING_HI_SequenceFilePathTooLong(baseDir, fileName); | |
| 191 | ✗ | return false; | |
| 192 | } | ||
| 193 | |||
| 194 | ✗ | return true; | |
| 195 | ✗ | } | |
| 196 | |||
| 197 | namespace { | ||
| 198 | struct WasmFileReader { | ||
| 199 | ✗ | explicit WasmFileReader(Os::File& loadFile) : m_loadFile(loadFile) {} | |
| 200 | |||
| 201 | ✗ | spacewasm_read_result_t readChunk(const U8** outBuf, std::size_t* outLen) { | |
| 202 | ✗ | FwSizeType size = sizeof(this->m_readBuf); | |
| 203 | ✗ | const Os::File::Status status = this->m_loadFile.read(this->m_readBuf, size); | |
| 204 | |||
| 205 | spacewasm_read_result_t readStatus; | ||
| 206 | ✗ | if (status != Os::File::Status::OP_OK) { | |
| 207 | ✗ | *outLen = 0; | |
| 208 | ✗ | readStatus = SPACEWASM_READ_ERROR; | |
| 209 | } else { | ||
| 210 | // `size` is updated in-place with the number of bytes actually read. | ||
| 211 | ✗ | *outBuf = this->m_readBuf; | |
| 212 | ✗ | *outLen = static_cast<std::size_t>(size); | |
| 213 | |||
| 214 | ✗ | if (size == 0) { | |
| 215 | ✗ | readStatus = SPACEWASM_READ_EOF; | |
| 216 | } else { | ||
| 217 | ✗ | readStatus = SPACEWASM_READ_OK; | |
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | ✗ | return readStatus; | |
| 222 | } | ||
| 223 | |||
| 224 | ✗ | static spacewasm_read_result_t readChunkCallback(void* userdata, const U8** outBuf, size_t* outLen) { | |
| 225 | ✗ | FW_ASSERT(userdata != nullptr); | |
| 226 | ✗ | return static_cast<WasmFileReader*>(userdata)->readChunk(outBuf, outLen); | |
| 227 | } | ||
| 228 | |||
| 229 | //! Currently loading file handle | ||
| 230 | Os::File& m_loadFile; | ||
| 231 | |||
| 232 | //! Buffer handed to the streaming loader, filled from `m_loadFile`. | ||
| 233 | U8 m_readBuf[Svc::WasmSequencerConfig::LOAD_READ_CHUNK_SIZE]{}; | ||
| 234 | }; | ||
| 235 | } // namespace | ||
| 236 | |||
| 237 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_load( | |
| 238 | SmId smId, | ||
| 239 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 240 | const Svc::WasmSequencer_LoadRequest& value) { | ||
| 241 | ✗ | FW_ASSERT(this->m_wasm != nullptr); | |
| 242 | ✗ | FW_ASSERT(this->m_guest_allocator != nullptr); | |
| 243 | |||
| 244 | // Resolve the sequence file path against SEQ_BASE_DIR | ||
| 245 | ✗ | Fw::String filePath; | |
| 246 | ✗ | if (!this->resolveSequencePath(value.get_fileName(), filePath)) { | |
| 247 | ✗ | this->controller_sendSignal_loadFailed(value.get_context()); | |
| 248 | ✗ | return; | |
| 249 | } | ||
| 250 | |||
| 251 | // Record the sequence name for telemetry (module name, or filename stem). | ||
| 252 | ✗ | this->setSequenceName(filePath, value.get_moduleName()); | |
| 253 | |||
| 254 | ✗ | Os::File file; | |
| 255 | ✗ | const Os::File::Status openStatus = file.open(filePath.toChar(), Os::File::OPEN_READ); | |
| 256 | ✗ | if (openStatus != Os::File::Status::OP_OK) { | |
| 257 | ✗ | this->log_WARNING_HI_FileOpenError(filePath, openStatus); | |
| 258 | ✗ | this->controller_sendSignal_loadFailed(value.get_context()); | |
| 259 | ✗ | return; | |
| 260 | } | ||
| 261 | |||
| 262 | ✗ | this->m_lastLoadFileName = value.get_fileName(); | |
| 263 | |||
| 264 | ✗ | this->takeAllocatorLock(); | |
| 265 | |||
| 266 | ✗ | U32 moduleIndex = 0; | |
| 267 | ✗ | Svc::WasmSequencer_RequestContext next = value.get_context(); | |
| 268 | |||
| 269 | ✗ | WasmFileReader reader(file); | |
| 270 | |||
| 271 | auto status = | ||
| 272 | ✗ | spacewasm_load_module(this->m_wasm, value.get_moduleName().toChar(), &WasmFileReader::readChunkCallback, | |
| 273 | &reader, this->m_guest_allocator, &moduleIndex); | ||
| 274 | |||
| 275 | ✗ | next.set_moduleIdx(static_cast<WasmSequencer_ModuleIdx>(moduleIndex)); | |
| 276 | |||
| 277 | ✗ | this->releaseAllocatorLock(); | |
| 278 | |||
| 279 | ✗ | file.close(); | |
| 280 | |||
| 281 | ✗ | if (status == SPACEWASM_OK) { | |
| 282 | ✗ | this->controller_sendSignal_loadSucceeded(next); | |
| 283 | } else { | ||
| 284 | ✗ | this->log_WARNING_HI_ModuleLoadFailed(WasmSequencer_Status(status)); | |
| 285 | ✗ | this->controller_sendSignal_loadFailed(value.get_context()); | |
| 286 | } | ||
| 287 | ✗ | } | |
| 288 | |||
| 289 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_invokeStart( | |
| 290 | SmId smId, | ||
| 291 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 292 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 293 | U32 start_module_idx; | ||
| 294 | U32 start_function_idx; | ||
| 295 | |||
| 296 | // Get the module's start function WasmRef | ||
| 297 | ✗ | auto status = spacewasm_module_start(this->m_wasm, value.get_moduleIdx(), &start_module_idx, &start_function_idx); | |
| 298 | ✗ | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 299 | |||
| 300 | ✗ | this->m_invokeStatus = spacewasm_invoke(this->m_wasm, start_module_idx, start_function_idx, nullptr, 0); | |
| 301 | ✗ | } | |
| 302 | |||
| 303 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_invokeMain( | |
| 304 | SmId smId, | ||
| 305 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 306 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 307 | // Resolve the main function of given module index | ||
| 308 | ✗ | U32 funcIndex = 0; | |
| 309 | ✗ | auto status = spacewasm_find_export_func(this->m_wasm, static_cast<U32>(value.get_moduleIdx()), "main", &funcIndex); | |
| 310 | |||
| 311 | // This should always succeed because our state machine is checking whether this module has a main/is-valid | ||
| 312 | ✗ | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 313 | |||
| 314 | ✗ | this->m_invokeStatus = | |
| 315 | ✗ | spacewasm_invoke(this->m_wasm, static_cast<U32>(value.get_moduleIdx()), funcIndex, nullptr, 0); | |
| 316 | ✗ | } | |
| 317 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleInvalidMain( | |
| 318 | SmId smId, | ||
| 319 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 320 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 321 | // Get the failure status of why the main module is invalid | ||
| 322 | ✗ | auto problemStatus = this->validateModuleMain(value.get_moduleIdx()); | |
| 323 | ✗ | this->log_WARNING_HI_InvalidModuleEntrypoint(value.get_moduleIdx(), WasmSequencer_Status(problemStatus)); | |
| 324 | ✗ | } | |
| 325 | |||
| 326 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleMainInvokeFailed( | |
| 327 | SmId smId, | ||
| 328 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 329 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 330 | ✗ | this->log_WARNING_HI_ModuleMainInvokeFailed(WasmSequencer_Status(this->m_invokeStatus)); | |
| 331 | ✗ | } | |
| 332 | |||
| 333 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleStartInvokeFailed( | |
| 334 | SmId smId, | ||
| 335 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 336 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 337 | ✗ | this->log_WARNING_HI_ModuleStartInvokeFailed(WasmSequencer_Status(this->m_invokeStatus)); | |
| 338 | ✗ | } | |
| 339 | |||
| 340 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleStarted( | |
| 341 | SmId smId, | ||
| 342 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 343 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 344 | ✗ | this->log_ACTIVITY_HI_SequenceStarting(value.get_moduleIdx()); | |
| 345 | ✗ | if (value.get_source() == Svc::WasmSequencer_SignalSource::COMMAND_RUN || | |
| 346 | ✗ | value.get_source() == Svc::WasmSequencer_SignalSource::PORT_RUN) { | |
| 347 | ✗ | if (this->isConnected_seqStartOut_OutputPort(0)) { | |
| 348 | ✗ | this->seqStartOut_out(0, this->m_lastLoadFileName, Svc::SeqArgs{}); | |
| 349 | } | ||
| 350 | } | ||
| 351 | ✗ | } | |
| 352 | |||
| 353 | 1 | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_resetStore( | |
| 354 | SmId smId, | ||
| 355 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) { | ||
| 356 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (this->m_wasm == nullptr) { |
| 357 | // The store has not been created yet. | ||
| 358 | // This should be the initial transition | ||
| 359 | 1 | FW_ASSERT(signal == Svc_WasmSequencer_ControllerStateMachine::Signal::__FPRIME_INITIAL_TRANSITION, | |
| 360 | static_cast<FwAssertArgType>(signal)); | ||
| 361 | |||
| 362 | // `configure()` will initialize the first store | ||
| 363 | } else { | ||
| 364 | ✗ | this->destroyStore(); | |
| 365 | ✗ | this->createStore(); | |
| 366 | } | ||
| 367 | 1 | } | |
| 368 | |||
| 369 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_runEngine( | |
| 370 | SmId smId, | ||
| 371 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 372 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 373 | ✗ | this->interpreter_sendSignal_run(value); | |
| 374 | ✗ | } | |
| 375 | |||
| 376 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleSucceeded( | |
| 377 | SmId smId, | ||
| 378 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 379 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 380 | ✗ | this->m_tlm.sequencesSucceeded++; | |
| 381 | ✗ | this->log_ACTIVITY_HI_SequenceSucceeded(value.get_moduleIdx()); | |
| 382 | ✗ | } | |
| 383 | |||
| 384 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleStartFailed( | |
| 385 | SmId smId, | ||
| 386 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 387 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 388 | // A failure while running a module's start function (LOAD-with-start, or the | ||
| 389 | // start phase of a RUN-with-start). | ||
| 390 | ✗ | this->reportSequenceRuntimeFailure(value.get_moduleIdx(), WasmSequencer_SequencePhase::START); | |
| 391 | ✗ | } | |
| 392 | |||
| 393 | ✗ | void WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_action_reportModuleMainFailed( | |
| 394 | SmId smId, | ||
| 395 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 396 | const Svc::WasmSequencer_RequestContext& value) { | ||
| 397 | // A failure while running a module's main function. | ||
| 398 | ✗ | this->reportSequenceRuntimeFailure(value.get_moduleIdx(), WasmSequencer_SequencePhase::MAIN); | |
| 399 | ✗ | } | |
| 400 | |||
| 401 | ✗ | void WasmSequencer ::reportSequenceRuntimeFailure(WasmSequencer_ModuleIdx moduleIdx, | |
| 402 | WasmSequencer_SequencePhase phase) { | ||
| 403 | ✗ | switch (this->m_exit.reason) { | |
| 404 | ✗ | case WasmSequencer_ExitReason::CANCEL: | |
| 405 | ✗ | this->m_tlm.sequencesCancelled++; | |
| 406 | ✗ | this->log_ACTIVITY_HI_SequenceCancelled(moduleIdx, phase); | |
| 407 | ✗ | break; | |
| 408 | ✗ | case WasmSequencer_ExitReason::HOST_PANIC: | |
| 409 | ✗ | this->m_tlm.sequencesFailed++; | |
| 410 | ✗ | this->log_WARNING_HI_SequencePanic(moduleIdx, phase, this->m_exit.code); | |
| 411 | ✗ | break; | |
| 412 | ✗ | case WasmSequencer_ExitReason::INTERPRETER_TRAP: | |
| 413 | ✗ | this->m_tlm.sequencesFailed++; | |
| 414 | ✗ | this->log_WARNING_HI_SequenceTrapped(moduleIdx, phase, this->m_exit.lastTrapReason); | |
| 415 | ✗ | break; | |
| 416 | ✗ | case WasmSequencer_ExitReason::INTERPRETER_FINISHED: // fallthrough is intended | |
| 417 | case WasmSequencer_ExitReason::HOST_EXIT: | ||
| 418 | // Only reached on the failure path: a non-zero main return value or a | ||
| 419 | // non-zero fprime.exit code. | ||
| 420 | ✗ | this->m_tlm.sequencesFailed++; | |
| 421 | ✗ | this->log_WARNING_HI_SequenceExited(moduleIdx, phase, this->m_exit.code); | |
| 422 | ✗ | break; | |
| 423 | ✗ | case WasmSequencer_ExitReason::UNKNOWN: | |
| 424 | case WasmSequencer_ExitReason::REPLY_TIMEOUT: | ||
| 425 | case WasmSequencer_ExitReason::HOST_FAILURE: | ||
| 426 | case WasmSequencer_ExitReason::UNEXPECTED_REPLY: | ||
| 427 | case WasmSequencer_ExitReason::TIMER_INCOMPARABLE: | ||
| 428 | default: | ||
| 429 | ✗ | this->m_tlm.sequencesFailed++; | |
| 430 | ✗ | this->log_WARNING_HI_SequenceHostFailure(moduleIdx, phase, this->m_exit.reason, | |
| 431 | ✗ | this->m_exit.lastHostFunction); | |
| 432 | ✗ | break; | |
| 433 | } | ||
| 434 | ✗ | } | |
| 435 | |||
| 436 | // ---------------------------------------------------------------------- | ||
| 437 | // Implementations for internal state machine guards | ||
| 438 | // ---------------------------------------------------------------------- | ||
| 439 | |||
| 440 | ✗ | bool WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_guard_moduleHasStart( | |
| 441 | SmId smId, | ||
| 442 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 443 | const Svc::WasmSequencer_RequestContext& value) const { | ||
| 444 | U32 start_module_idx; | ||
| 445 | U32 start_function_idx; | ||
| 446 | ✗ | auto status = spacewasm_module_start(this->m_wasm, static_cast<U32>(value.get_moduleIdx()), &start_module_idx, | |
| 447 | &start_function_idx); | ||
| 448 | |||
| 449 | ✗ | switch (status) { | |
| 450 | ✗ | case SPACEWASM_OK: | |
| 451 | ✗ | return true; | |
| 452 | ✗ | case SPACEWASM_ERR_NOT_FOUND: | |
| 453 | ✗ | return false; | |
| 454 | ✗ | default: | |
| 455 | // All other status means we passed the c api invalid options | ||
| 456 | ✗ | FW_ASSERT(false, status); | |
| 457 | ✗ | return false; | |
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | ✗ | bool WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_guard_moduleHasValidMain( | |
| 462 | SmId smId, | ||
| 463 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 464 | const Svc::WasmSequencer_RequestContext& value) const { | ||
| 465 | ✗ | return this->validateModuleMain(value.get_moduleIdx()) == SPACEWASM_OK; | |
| 466 | } | ||
| 467 | |||
| 468 | ✗ | bool WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_guard_invokeSucceeded( | |
| 469 | SmId smId, | ||
| 470 | Svc_WasmSequencer_ControllerStateMachine::Signal signal, | ||
| 471 | const Svc::WasmSequencer_RequestContext& value) const { | ||
| 472 | ✗ | return this->m_invokeStatus == SPACEWASM_OK; | |
| 473 | } | ||
| 474 | |||
| 475 | ✗ | bool WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_guard_interpreterSucceeded( | |
| 476 | SmId smId, | ||
| 477 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) const { | ||
| 478 | // The engine sets m_exit.reason and m_exit.code before signalling | ||
| 479 | // engineFinished. A run succeeds only when it finished with a zero code: | ||
| 480 | // main returned 0 (a void return reports code 0) or the guest called | ||
| 481 | // fprime.exit(0) | ||
| 482 | ✗ | switch (this->m_exit.reason) { | |
| 483 | ✗ | case WasmSequencer_ExitReason::INTERPRETER_FINISHED: | |
| 484 | case WasmSequencer_ExitReason::HOST_EXIT: | ||
| 485 | ✗ | return this->m_exit.code == 0; | |
| 486 | ✗ | default: | |
| 487 | ✗ | return false; | |
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | ✗ | bool WasmSequencer ::Svc_WasmSequencer_ControllerStateMachine_guard_cancelRequested( | |
| 492 | SmId smId, | ||
| 493 | Svc_WasmSequencer_ControllerStateMachine::Signal signal) const { | ||
| 494 | ✗ | return this->m_cancelRequested; | |
| 495 | } | ||
| 496 | |||
| 497 | } // namespace Svc | ||
| 498 |