| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title WasmSequencerHelpers.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief cpp file for WasmSequencer component implementation class helpers | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include "Fw/Types/Assert.hpp" | ||
| 9 | #include "Fw/Types/StringBase.hpp" | ||
| 10 | #include "Os/Console.hpp" | ||
| 11 | #include "Svc/WasmSequencer/WasmSequencer.hpp" | ||
| 12 | #include "Svc/WasmSequencer/WasmSequencer_MemoryGrowFailReasonEnumAc.hpp" | ||
| 13 | #include "Svc/WasmSequencer/fprime_spacewasm/include/fprime_spacewasm.h" | ||
| 14 | #include "config/FwAssertArgTypeAliasAc.h" | ||
| 15 | #include "config/FwSizeTypeAliasAc.h" | ||
| 16 | #include "config/WasmSequencerConfig.hpp" | ||
| 17 | #include "spacewasm.h" | ||
| 18 | |||
| 19 | namespace Svc { | ||
| 20 | // ---------------------------------------------------------------------- | ||
| 21 | // Interpreter store and page-backed allocators | ||
| 22 | // ---------------------------------------------------------------------- | ||
| 23 | |||
| 24 | 339 | U8* WasmSequencer ::globalAlloc(const U32 size, const U32 align) { | |
| 25 | // The spacewasm PageAllocator only ever requests fixed-size pages of exactly | ||
| 26 | // SPACEWASM_PAGE_SIZE, aligned no more than the pool's alignment. | ||
| 27 | 339 | FW_ASSERT(size == Svc::WasmSequencerConfig::SPACEWASM_PAGE_SIZE, static_cast<FwAssertArgType>(size)); | |
| 28 | 339 | FW_ASSERT(align <= 8, static_cast<FwAssertArgType>(align)); | |
| 29 | 339 | FW_ASSERT(!this->m_heapPoisoned); | |
| 30 | |||
| 31 |
1/2✓ Branch 8 taken 339 times.
✗ Branch 9 not taken.
|
339 | if (this->m_heapPagesUsed < this->m_config.heapPages) { |
| 32 | 339 | auto page = this->m_heapPages[this->m_heapPagesUsed]; | |
| 33 | 339 | FW_ASSERT(page != nullptr); | |
| 34 | 339 | this->m_heapPagesUsed += 1; | |
| 35 | 339 | return page; | |
| 36 | } else { | ||
| 37 | // Out of pages. | ||
| 38 | ✗ | return nullptr; | |
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | 339 | void WasmSequencer ::globalDealloc(const U8* ptr) { | |
| 43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 339 times.
|
339 | if (ptr == nullptr) { |
| 44 | ✗ | return; | |
| 45 | } | ||
| 46 | |||
| 47 | // Make sure the pointer that was given back to us is ours | ||
| 48 | 339 | bool found = false; | |
| 49 |
1/2✓ Branch 4 taken 339 times.
✗ Branch 5 not taken.
|
339 | for (FwSizeType i = 0; i < this->m_config.heapPages; i++) { |
| 50 |
1/2✓ Branch 6 taken 339 times.
✗ Branch 7 not taken.
|
339 | if (ptr == this->m_heapPages[i]) { |
| 51 | 339 | found = true; | |
| 52 | 339 | break; | |
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | 339 | FW_ASSERT(found); | |
| 57 | |||
| 58 | // Decrement the number of pages used. | ||
| 59 | // Deallocation only happens when the store is being destroyed. | ||
| 60 | // We will assert that the used page count drops to zero once store destruction completes | ||
| 61 | 339 | FW_ASSERT(this->m_heapPagesUsed > 0); | |
| 62 | 339 | this->m_heapPagesUsed -= 1; | |
| 63 | 339 | this->m_heapPoisoned = true; | |
| 64 | } | ||
| 65 | |||
| 66 | 202 | U8* WasmSequencer ::guestAlloc(FwSizeType size, U32 align) { | |
| 67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 202 times.
|
202 | if (size == 0) { |
| 68 | ✗ | return nullptr; | |
| 69 | } | ||
| 70 | |||
| 71 | // Reject any request that cannot possibly fit the guest pool up front. | ||
| 72 |
3/4✓ Branch 4 taken 201 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 201 times.
|
202 | if (size > this->m_config.guestMemorySize || align > SPACEWASM_MEMORY_ALIGNMENT) { |
| 73 | 1 | return nullptr; | |
| 74 | } | ||
| 75 | |||
| 76 | // Round the current offset up to the requested alignment | ||
| 77 |
1/2✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
|
201 | const FwSizeType a = (align < 1) ? 1 : static_cast<FwSizeType>(align); |
| 78 | 201 | const FwSizeType start = (this->m_guestPoolOffset + a - 1) & ~(a - 1); | |
| 79 | |||
| 80 | // Compare against the pre-subtracted bound so `start + size` cannot overflow. | ||
| 81 | // `size <= this->m_config.guestMemorySize` (checked above) makes the subtraction non-negative. | ||
| 82 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 201 times.
|
201 | if (start > this->m_config.guestMemorySize - size) { |
| 83 | ✗ | return nullptr; | |
| 84 | } | ||
| 85 | 201 | this->m_guestPoolOffset = start + size; | |
| 86 | 201 | return &this->m_guestPool[start]; | |
| 87 | } | ||
| 88 | |||
| 89 | 8 | U8* WasmSequencer ::guestRealloc(U8* ptr, FwSizeType oldSize, FwSizeType newSize, U32 align) { | |
| 90 | // We have received a memory.grow request. | ||
| 91 | // The following MUST be true for us to actually give the guest it's memory (otherwise fail): | ||
| 92 | // 1. This linear memory is the final allocated memory in the guest bump allocator | ||
| 93 | // 2. We have enough space free to actually do this request | ||
| 94 | // 3. You buy me a donut. | ||
| 95 | |||
| 96 | 8 | std::ptrdiff_t moduleMemOffset = ptr - this->m_guestPool; | |
| 97 | |||
| 98 | // The pointer must lie within our guest pool. | ||
| 99 | 8 | FW_ASSERT(moduleMemOffset >= 0, static_cast<FwAssertArgType>(moduleMemOffset)); | |
| 100 | 8 | const FwSizeType offset = static_cast<FwSizeType>(moduleMemOffset); | |
| 101 | |||
| 102 | // Check 1. i.e. the pointer is what we expect given old_size and current guest offset | ||
| 103 |
2/2✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
|
8 | if (offset + oldSize == this->m_guestPoolOffset) { |
| 104 | // Check 2. We have enough space in the guest pool to service this request | ||
| 105 |
2/2✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
|
6 | if (offset + newSize <= this->m_config.guestMemorySize) { |
| 106 | // ...now the donuts | ||
| 107 | // Allocate the new guest memory size | ||
| 108 | 3 | this->m_guestPoolOffset = offset + newSize; | |
| 109 | |||
| 110 | // We return the same pointer since this is a strict grow and | ||
| 111 | // we already reserved the slot in front of this memory | ||
| 112 | 3 | return ptr; | |
| 113 | } | ||
| 114 | |||
| 115 | // We are the last allocation, but the grown size does not fit the guest pool. | ||
| 116 |
2/2✓ Branch 6 taken 3 times.
✓ Branch 10 taken 3 times.
|
3 | this->log_WARNING_LO_MemoryGrowRejected(WasmSequencer_MemoryGrowFailReason::INSUFFICIENT_POOL_SPACE, |
| 117 | static_cast<U64>(newSize)); | ||
| 118 | 3 | return nullptr; | |
| 119 | } | ||
| 120 | |||
| 121 | // We are not the last allocation in the bump pool, so we cannot grow in place. | ||
| 122 |
2/2✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
|
2 | this->log_WARNING_LO_MemoryGrowRejected(WasmSequencer_MemoryGrowFailReason::NOT_LAST_ALLOCATION, |
| 123 | static_cast<U64>(newSize)); | ||
| 124 | 2 | return nullptr; | |
| 125 | } | ||
| 126 | |||
| 127 | 195 | void WasmSequencer ::guestDealloc(const U8* ptr, const FwSizeType size) { | |
| 128 | // Bump allocator: individual frees are no-ops. The whole guest pool is reset | ||
| 129 | // when a new store is created (destroyStore). | ||
| 130 | (void)ptr; | ||
| 131 | (void)size; | ||
| 132 | 195 | } | |
| 133 | |||
| 134 | 196 | U8* WasmSequencer ::guestAllocCallback(void* userdata, size_t size, size_t align) { | |
| 135 | 196 | FW_ASSERT(userdata != nullptr); | |
| 136 | 196 | return static_cast<WasmSequencer*>(userdata)->guestAlloc(static_cast<FwSizeType>(size), static_cast<U32>(align)); | |
| 137 | } | ||
| 138 | |||
| 139 | 4 | U8* WasmSequencer ::guestReallocCallback(void* userdata, U8* ptr, size_t old_size, size_t new_size, size_t align) { | |
| 140 | 4 | FW_ASSERT(userdata != nullptr); | |
| 141 | // Do NOT narrow old_size/new_size to U32: a 4 GiB grow (new_size == 2^32) would alias to 0 and | ||
| 142 | // be accepted as a no-op grow. Pass full width; guestRealloc's fits-pool check rejects it. | ||
| 143 | 4 | return static_cast<WasmSequencer*>(userdata)->guestRealloc( | |
| 144 | 4 | ptr, static_cast<FwSizeType>(old_size), static_cast<FwSizeType>(new_size), static_cast<U32>(align)); | |
| 145 | } | ||
| 146 | |||
| 147 | 195 | void WasmSequencer ::guestDeallocCallback(void* userdata, U8* ptr, size_t size, size_t align) { | |
| 148 | 195 | FW_ASSERT(userdata != nullptr); | |
| 149 | (void)align; | ||
| 150 | 195 | static_cast<WasmSequencer*>(userdata)->guestDealloc(ptr, static_cast<FwSizeType>(size)); | |
| 151 | 195 | } | |
| 152 | |||
| 153 | 339 | void WasmSequencer ::createStore() { | |
| 154 | 339 | FW_ASSERT(this->m_wasm == nullptr); | |
| 155 | |||
| 156 |
1/1✓ Branch 4 taken 339 times.
|
339 | this->takeAllocatorLock(); |
| 157 | |||
| 158 | 339 | spacewasm_host_t host; | |
| 159 |
1/1✓ Branch 1 taken 339 times.
|
339 | spacewasm_status_t status = spacewasm_host_new(1, &host); |
| 160 | 339 | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 161 | |||
| 162 |
1/1✓ Branch 4 taken 339 times.
|
339 | this->hostFprimeV1(&host); |
| 163 | |||
| 164 | 339 | spacewasm_compiler_options_t options; | |
| 165 | 339 | options.allow_memory_grow = true; // implemented in a restricted way (see guestRealloc) | |
| 166 | 339 | options.max_backpatch_iterations = Svc::WasmSequencerConfig::MAX_BACKPATCH_ITERATIONS; | |
| 167 | 339 | options.max_code_pages = this->m_config.maxCodePages; | |
| 168 | |||
| 169 |
1/1✓ Branch 11 taken 339 times.
|
339 | status = spacewasm_new(&host, this->m_config.stackSize, this->m_config.maxGuestModules, options, &this->m_wasm); |
| 170 | |||
| 171 | 678 | this->m_guest_allocator = | |
| 172 |
1/1✓ Branch 1 taken 339 times.
|
339 | spacewasm_allocator_new(&WasmSequencer::guestAllocCallback, &WasmSequencer::guestReallocCallback, |
| 173 | &WasmSequencer::guestDeallocCallback, /* userdata */ this); | ||
| 174 | |||
| 175 |
1/1✓ Branch 4 taken 339 times.
|
339 | this->releaseAllocatorLock(); |
| 176 | |||
| 177 | // Make sure the store allocation succeeded. | ||
| 178 | // Failure means the heap memory is too small to host this number of modules + Wasm stack... | ||
| 179 | // | ||
| 180 | // If status == SPACEWASM_ERR_PAGE_TOO_SMALL: | ||
| 181 | // - Increase Svc::WasmSequencerConfig::SPACEWASM_PAGE_SIZE | ||
| 182 | // If SPACEWASM_ERR_OUT_OF_MEMORY / SPACEWASM_ERR_ALLOC_FAILED: | ||
| 183 | // - Increase heapPages in configure() | ||
| 184 | // - Lower maxGuestModules in configure() | ||
| 185 | // - Lower stackSize in configure() | ||
| 186 | 339 | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 187 | |||
| 188 | // Make sure the guest allocator creation succeeded | ||
| 189 | 339 | FW_ASSERT(this->m_guest_allocator != nullptr); | |
| 190 | |||
| 191 |
1/1✓ Branch 9 taken 339 times.
|
339 | this->log_DIAGNOSTIC_StoreAllocationSucceeded(this->m_config.maxGuestModules); |
| 192 | 339 | } | |
| 193 | |||
| 194 | 339 | void WasmSequencer ::destroyStore() { | |
| 195 | 339 | FW_ASSERT(this->m_wasm != nullptr); | |
| 196 | 339 | FW_ASSERT(this->m_guest_allocator != nullptr); | |
| 197 | |||
| 198 | 339 | this->takeAllocatorLock(); | |
| 199 | 339 | spacewasm_destroy(this->m_wasm); | |
| 200 | 339 | spacewasm_allocator_destroy(this->m_guest_allocator); | |
| 201 | 339 | this->releaseAllocatorLock(); | |
| 202 | 339 | this->m_wasm = nullptr; | |
| 203 | 339 | this->m_guest_allocator = nullptr; | |
| 204 | |||
| 205 | // Make sure we cleanly deallocated all the heap memory | ||
| 206 | 339 | FW_ASSERT(this->m_heapPagesUsed == 0, static_cast<FwAssertArgType>(this->m_heapPagesUsed)); | |
| 207 | |||
| 208 | // Reset the guest linear-memory bump allocator; all guest allocations were | ||
| 209 | // owned by the store that just went away. | ||
| 210 | 339 | this->m_guestPoolOffset = 0; | |
| 211 | 339 | this->m_heapPoisoned = false; | |
| 212 | 339 | } | |
| 213 | |||
| 214 | 156 | spacewasm_status_t WasmSequencer ::validateModuleMain(WasmSequencer_ModuleIdx moduleIdx) const { | |
| 215 | 156 | FW_ASSERT(this->m_wasm != nullptr); | |
| 216 | |||
| 217 | 156 | U32 mainIndex; | |
| 218 |
1/1✓ Branch 5 taken 156 times.
|
156 | auto status = spacewasm_find_export_func(this->m_wasm, static_cast<U32>(moduleIdx), "main", &mainIndex); |
| 219 | |||
| 220 |
2/2✓ Branch 0 taken 150 times.
✓ Branch 1 taken 6 times.
|
156 | if (status == SPACEWASM_OK) { |
| 221 | // We accept both the [] -> [] and [] -> i32 main signatures. Checking | ||
| 222 | // against "" first yields PARAM_LEN_MISMATCH (not BAD_SIGNATURE) for an | ||
| 223 | // i32-returning main -- BAD_SIGNATURE only flags a malformed signature | ||
| 224 | // *string* -- so fall back on any mismatch, not just BAD_SIGNATURE. | ||
| 225 |
1/1✓ Branch 5 taken 150 times.
|
150 | status = spacewasm_check_func_signature(this->m_wasm, static_cast<U32>(moduleIdx), mainIndex, "", ""); |
| 226 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 149 times.
|
150 | if (status != SPACEWASM_OK) { |
| 227 |
1/1✓ Branch 5 taken 1 times.
|
1 | status = spacewasm_check_func_signature(this->m_wasm, static_cast<U32>(moduleIdx), mainIndex, "", "i"); |
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | 156 | return status; | |
| 232 | } | ||
| 233 | |||
| 234 | 22 | U32 WasmSequencer ::makeCmdUid() const { | |
| 235 | // cmdUid is formatted XXYY, where XX are the low 16 bits of m_sequencesStarted | ||
| 236 | // and YY are the low 16 bits of m_tlm.commandsDispatched. On the way back in via | ||
| 237 | // cmdResponseIn this lets us check A) that the response is from the current | ||
| 238 | // sequence (modulo 2^16) and B) that it is this exact command instance and not | ||
| 239 | // another dispatch of the same opcode. | ||
| 240 | 22 | return static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_tlm.commandsDispatched & 0xFFFF)); | |
| 241 | } | ||
| 242 | |||
| 243 | 227 | void WasmSequencer ::respondToRequest(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) { | |
| 244 |
2/3✓ Branch 4 taken 215 times.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
|
227 | switch (value.get_source()) { |
| 245 | 215 | case WasmSequencer_SignalSource::COMMAND_RUN: | |
| 246 | case WasmSequencer_SignalSource::COMMAND_INVOKE: | ||
| 247 | case WasmSequencer_SignalSource::COMMAND_LOAD: | ||
| 248 | // The request originated from a command; answer it on cmdResponse. | ||
| 249 |
2/2✓ Branch 6 taken 215 times.
✓ Branch 25 taken 215 times.
|
215 | this->cmdResponse_out(value.get_cmdCtx().get_opcode(), value.get_cmdCtx().get_cmdSeq(), response); |
| 250 | 215 | break; | |
| 251 | 12 | case WasmSequencer_SignalSource::PORT_RUN: | |
| 252 | case WasmSequencer_SignalSource::PORT_INVOKE: | ||
| 253 | // Port-driven requests have no command response to send. | ||
| 254 | 12 | break; | |
| 255 | ✗ | default: | |
| 256 | ✗ | FW_ASSERT(false, static_cast<FwAssertArgType>(value.get_source())); | |
| 257 | ✗ | break; | |
| 258 | } | ||
| 259 | 227 | } | |
| 260 | |||
| 261 | 222 | void WasmSequencer ::respondToWaiting(const Fw::CmdResponse& response) { | |
| 262 | // Drain every WAIT command blocked on sequence completion, answering each. | ||
| 263 | 222 | WaitingCmd cmd{}; | |
| 264 |
3/3✓ Branch 4 taken 235 times.
✓ Branch 13 taken 13 times.
✓ Branch 14 taken 222 times.
|
235 | while (this->m_waiting.dequeue(cmd) == Fw::Success::SUCCESS) { |
| 265 |
2/2✓ Branch 6 taken 13 times.
✓ Branch 9 taken 13 times.
|
13 | this->cmdResponse_out(cmd.opCode, cmd.cmdSeq, response); |
| 266 | } | ||
| 267 | 222 | } | |
| 268 | |||
| 269 | 196 | void WasmSequencer ::reportSeqDone(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) { | |
| 270 | // seqStart/seqDone are RUN-scoped: seqStartOut is only emitted for RUN sources | ||
| 271 | // (see reportModuleStarted), so only emit the matching seqDoneOut for those. | ||
| 272 | // A non-RUN completion (INVOKE / LOAD) reports neither, keeping the pair balanced. | ||
| 273 |
6/6✓ Branch 4 taken 66 times.
✓ Branch 5 taken 130 times.
✓ Branch 6 taken 54 times.
✓ Branch 7 taken 12 times.
✓ Branch 8 taken 54 times.
✓ Branch 9 taken 142 times.
|
262 | if (value.get_source() != Svc::WasmSequencer_SignalSource::COMMAND_RUN && |
| 274 | 66 | value.get_source() != Svc::WasmSequencer_SignalSource::PORT_RUN) { | |
| 275 | 54 | return; | |
| 276 | } | ||
| 277 |
2/2✓ Branch 5 taken 140 times.
✓ Branch 6 taken 2 times.
|
142 | if (this->isConnected_seqDoneOut_OutputPort(0)) { |
| 278 | 140 | this->seqDoneOut_out(0, 0, 0, response); | |
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | 38 | void WasmSequencer ::reportSeqAborted(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) { | |
| 283 | // Respond to port invokers that the sequence exited | ||
| 284 |
2/2✓ Branch 4 taken 9 times.
✓ Branch 5 taken 29 times.
|
38 | if (value.get_source() == Svc::WasmSequencer_SignalSource::PORT_RUN) { |
| 285 | 9 | this->reportSeqDone(value, response); | |
| 286 | } | ||
| 287 | 38 | } | |
| 288 | |||
| 289 | 18 | spacewasm_status_t WasmSequencer ::setGlobal(const Fw::StringBase& moduleName, | |
| 290 | const Fw::StringBase& name, | ||
| 291 | spacewasm_value_t value) { | ||
| 292 | 18 | U32 moduleIdx = 0; | |
| 293 |
2/2✓ Branch 11 taken 18 times.
✓ Branch 18 taken 18 times.
|
18 | spacewasm_status_t status = spacewasm_find_module(this->m_wasm, moduleName.toChar(), &moduleIdx); |
| 294 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
|
18 | if (status != SPACEWASM_OK) { |
| 295 | 2 | return status; | |
| 296 | } | ||
| 297 | |||
| 298 | 16 | U32 globalIdx; | |
| 299 |
2/2✓ Branch 11 taken 16 times.
✓ Branch 18 taken 16 times.
|
16 | status = spacewasm_find_global(this->m_wasm, moduleIdx, name.toChar(), &globalIdx); |
| 300 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
|
16 | if (status != SPACEWASM_OK) { |
| 301 | 1 | return status; | |
| 302 | } | ||
| 303 | |||
| 304 |
1/1✓ Branch 5 taken 15 times.
|
15 | return spacewasm_set_global(this->m_wasm, moduleIdx, globalIdx, value); |
| 305 | } | ||
| 306 | |||
| 307 | 25 | spacewasm_status_t WasmSequencer ::getGlobal(const Fw::StringBase& moduleName, | |
| 308 | const Fw::StringBase& name, | ||
| 309 | spacewasm_value_t& value) { | ||
| 310 | 25 | U32 moduleIdx = 0; | |
| 311 |
2/2✓ Branch 11 taken 25 times.
✓ Branch 18 taken 25 times.
|
25 | spacewasm_status_t status = spacewasm_find_module(this->m_wasm, moduleName.toChar(), &moduleIdx); |
| 312 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
|
25 | if (status != SPACEWASM_OK) { |
| 313 | 3 | return status; | |
| 314 | } | ||
| 315 | |||
| 316 | 22 | U32 globalIdx; | |
| 317 |
2/2✓ Branch 11 taken 22 times.
✓ Branch 18 taken 22 times.
|
22 | status = spacewasm_find_global(this->m_wasm, moduleIdx, name.toChar(), &globalIdx); |
| 318 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
|
22 | if (status != SPACEWASM_OK) { |
| 319 | 1 | return status; | |
| 320 | } | ||
| 321 | |||
| 322 |
1/1✓ Branch 5 taken 21 times.
|
21 | return spacewasm_get_global(this->m_wasm, moduleIdx, globalIdx, &value); |
| 323 | } | ||
| 324 | |||
| 325 | 51 | Svc::WasmSequencer_TrapReason::T WasmSequencer ::mapTrapReason(spacewasm_trap_t trap) { | |
| 326 | // spacewasm_trap_t values 0..14 map 1:1 onto the TrapReason enum ordinals. | ||
| 327 |
16/16✓ Branch 0 taken 14 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 1 times.
|
51 | switch (trap) { |
| 328 | 14 | case SPACEWASM_TRAP_UNREACHABLE: | |
| 329 | 14 | return Svc::WasmSequencer_TrapReason::UNREACHABLE; | |
| 330 | 22 | case SPACEWASM_TRAP_HOST: | |
| 331 | 22 | return Svc::WasmSequencer_TrapReason::HOST; | |
| 332 | 2 | case SPACEWASM_TRAP_DIVIDE_BY_ZERO: | |
| 333 | 2 | return Svc::WasmSequencer_TrapReason::DIVIDE_BY_ZERO; | |
| 334 | 1 | case SPACEWASM_TRAP_INVALID_TABLE_INDEX: | |
| 335 | 1 | return Svc::WasmSequencer_TrapReason::INVALID_TABLE_INDEX; | |
| 336 | 1 | case SPACEWASM_TRAP_INVALID_TABLE_FUNCTION_TYPE: | |
| 337 | 1 | return Svc::WasmSequencer_TrapReason::INVALID_TABLE_FUNCTION_TYPE; | |
| 338 | 1 | case SPACEWASM_TRAP_UNINITIALIZED_TABLE_ELEMENT: | |
| 339 | 1 | return Svc::WasmSequencer_TrapReason::UNINITIALIZED_TABLE_ELEMENT; | |
| 340 | 1 | case SPACEWASM_TRAP_GLOBAL_GET_FAILED: | |
| 341 | 1 | return Svc::WasmSequencer_TrapReason::GLOBAL_GET_FAILED; | |
| 342 | 1 | case SPACEWASM_TRAP_GLOBAL_SET_FAILED: | |
| 343 | 1 | return Svc::WasmSequencer_TrapReason::GLOBAL_SET_FAILED; | |
| 344 | 1 | case SPACEWASM_TRAP_OUT_OF_MEMORY: | |
| 345 | 1 | return Svc::WasmSequencer_TrapReason::OUT_OF_MEMORY; | |
| 346 | 1 | case SPACEWASM_TRAP_MEMORY_REF_NOT_UNIQUE: | |
| 347 | 1 | return Svc::WasmSequencer_TrapReason::MEMORY_REF_NOT_UNIQUE; | |
| 348 | 1 | case SPACEWASM_TRAP_MEMORY_OUT_OF_BOUNDS: | |
| 349 | 1 | return Svc::WasmSequencer_TrapReason::MEMORY_OUT_OF_BOUNDS; | |
| 350 | 1 | case SPACEWASM_TRAP_STACK_OVERFLOW: | |
| 351 | 1 | return Svc::WasmSequencer_TrapReason::STACK_OVERFLOW; | |
| 352 | 1 | case SPACEWASM_TRAP_UNREPRESENTABLE_RESULT: | |
| 353 | 1 | return Svc::WasmSequencer_TrapReason::UNREPRESENTABLE_RESULT; | |
| 354 | 1 | case SPACEWASM_TRAP_INTEGER_OVERFLOW: | |
| 355 | 1 | return Svc::WasmSequencer_TrapReason::INTEGER_OVERFLOW; | |
| 356 | 1 | case SPACEWASM_TRAP_BAD_CONVERSION_TO_INTEGER: | |
| 357 | 1 | return Svc::WasmSequencer_TrapReason::BAD_CONVERSION_TO_INTEGER; | |
| 358 | 1 | default: | |
| 359 | 1 | return Svc::WasmSequencer_TrapReason::HOST; | |
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | 210 | void WasmSequencer ::setSequenceName(const Fw::StringBase& filePath, const Fw::StringBase& moduleName) { | |
| 364 | // A non-empty module name was supplied to LOAD; use it verbatim. | ||
| 365 |
3/3✓ Branch 11 taken 210 times.
✓ Branch 13 taken 8 times.
✓ Branch 14 taken 202 times.
|
210 | if (moduleName.length() > 0) { |
| 366 |
1/1✓ Branch 9 taken 8 times.
|
8 | this->m_tlm.sequenceName = moduleName; |
| 367 | 8 | return; | |
| 368 | } | ||
| 369 | |||
| 370 | // RUN / LOAD: derive from the file's basename with any ".wasm" suffix stripped. | ||
| 371 |
1/1✓ Branch 11 taken 202 times.
|
202 | const char* const path = filePath.toChar(); |
| 372 |
1/1✓ Branch 11 taken 202 times.
|
202 | const FwSizeType len = static_cast<FwSizeType>(filePath.length()); |
| 373 | |||
| 374 | // Find the start of the basename (character after the last '/'). | ||
| 375 | 202 | FwSizeType nameLen = 0; | |
| 376 |
1/1✓ Branch 1 taken 202 times.
|
202 | const char* const base = WasmSequencer::pathBaseName(path, len, nameLen); |
| 377 | |||
| 378 | // Drop a trailing ".wasm" if present. | ||
| 379 | static const char suffix[] = ".wasm"; | ||
| 380 | 202 | const FwSizeType suffixLen = static_cast<FwSizeType>(sizeof(suffix) - 1); | |
| 381 |
1/2✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
|
202 | if (nameLen >= suffixLen) { |
| 382 | 202 | bool match = true; | |
| 383 |
2/2✓ Branch 0 taken 1010 times.
✓ Branch 1 taken 202 times.
|
1212 | for (FwSizeType i = 0; i < suffixLen; i++) { |
| 384 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 1010 times.
|
1010 | if (base[nameLen - suffixLen + i] != suffix[i]) { |
| 385 | ✗ | match = false; | |
| 386 | ✗ | break; | |
| 387 | } | ||
| 388 | } | ||
| 389 |
1/2✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
|
202 | if (match) { |
| 390 | 202 | nameLen -= suffixLen; | |
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | 202 | char name[FileNameStringSize]; | |
| 395 | 202 | FwSizeType n = 0; | |
| 396 |
3/4✓ Branch 0 taken 1655 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 1655 times.
✗ Branch 3 not taken.
|
1857 | for (FwSizeType i = 0; i < nameLen && n < static_cast<FwSizeType>(sizeof(name) - 1); i++) { |
| 397 | 1655 | name[n++] = base[i]; | |
| 398 | } | ||
| 399 | 202 | name[n] = '\0'; | |
| 400 |
1/1✓ Branch 6 taken 202 times.
|
202 | this->m_tlm.sequenceName = name; |
| 401 | } | ||
| 402 | |||
| 403 | 202 | const char* WasmSequencer ::pathBaseName(const char* path, FwSizeType len, FwSizeType& outLen) { | |
| 404 | 202 | FW_ASSERT(path != nullptr); | |
| 405 | // Basename starts just after the last '/', or at the start if there is none. | ||
| 406 | 202 | FwSizeType start = 0; | |
| 407 |
2/2✓ Branch 0 taken 2691 times.
✓ Branch 1 taken 202 times.
|
2893 | for (FwSizeType i = 0; i < len; i++) { |
| 408 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2688 times.
|
2691 | if (path[i] == '/') { |
| 409 | 3 | start = i + 1; | |
| 410 | } | ||
| 411 | } | ||
| 412 | 202 | outLen = len - start; | |
| 413 | 202 | return path + start; | |
| 414 | } | ||
| 415 | |||
| 416 | 5 | bool WasmSequencer ::pathHasParentTraversal(const Fw::StringBase& path) { | |
| 417 | 5 | const char* const s = path.toChar(); | |
| 418 | 5 | const FwSizeType len = static_cast<FwSizeType>(path.length()); | |
| 419 | |||
| 420 | // Walk the '/'-delimited segments; reject if any segment is exactly "..". | ||
| 421 | 5 | FwSizeType segStart = 0; | |
| 422 |
2/2✓ Branch 0 taken 267 times.
✓ Branch 1 taken 4 times.
|
271 | for (FwSizeType i = 0; i <= len; i++) { |
| 423 |
4/4✓ Branch 0 taken 263 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 262 times.
|
267 | if (i == len || s[i] == '/') { |
| 424 | 5 | const FwSizeType segLen = i - segStart; | |
| 425 |
4/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
5 | if (segLen == 2 && s[segStart] == '.' && s[segStart + 1] == '.') { |
| 426 | 1 | return true; | |
| 427 | } | ||
| 428 | 4 | segStart = i + 1; | |
| 429 | } | ||
| 430 | } | ||
| 431 | 4 | return false; | |
| 432 | } | ||
| 433 | |||
| 434 | 2656 | Os::Mutex* WasmSequencer::getGlobalAllocatorLock() { | |
| 435 | //! Process-wide lock serializing access to the spacewasm global-allocator registry. | ||
| 436 | //! Initialized by the first component instance's constructor | ||
| 437 |
4/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2655 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
2656 | static Os::Mutex s_globalAllocatorLock; |
| 438 | 2656 | return &s_globalAllocatorLock; | |
| 439 | } | ||
| 440 | |||
| 441 | 882 | void WasmSequencer ::takeAllocatorLock() { | |
| 442 | 882 | getGlobalAllocatorLock()->lock(); | |
| 443 | |||
| 444 | 882 | auto status = spacewasm_fprime_acquire_global_allocator(this); | |
| 445 | 882 | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 446 | 882 | } | |
| 447 | |||
| 448 | 882 | void WasmSequencer ::releaseAllocatorLock() { | |
| 449 | 882 | auto status = spacewasm_fprime_release_global_allocator(this); | |
| 450 | 882 | FW_ASSERT(status == SPACEWASM_OK, status); | |
| 451 | |||
| 452 | 882 | getGlobalAllocatorLock()->unlock(); | |
| 453 | 882 | } | |
| 454 | |||
| 455 | //! Panic hook the spacewasm interpreter calls on a fatal internal error. | ||
| 456 | // Must not return. | ||
| 457 | 1 | extern "C" void spacewasm_panic(const U8* filename, | |
| 458 | std::size_t filename_len, | ||
| 459 | U32 line, | ||
| 460 | const U8* msg, | ||
| 461 | std::size_t len) { | ||
| 462 |
1/1✓ Branch 2 taken 1 times.
|
1 | Fw::String fmtMsg; |
| 463 |
1/1✓ Branch 2 taken 1 times.
|
1 | (void)fmtMsg.format("Rust panic %.*s:%d: %.*s\n", static_cast<int>(filename_len), |
| 464 | reinterpret_cast<const char*>(filename), static_cast<int>(line), static_cast<int>(len), | ||
| 465 | reinterpret_cast<const char*>(msg)); | ||
| 466 |
1/1✓ Branch 1 taken 1 times.
|
1 | Os::Console::write(fmtMsg); |
| 467 | |||
| 468 | // Rust panics map to FSW assertions | ||
| 469 | 1 | FW_ASSERT(false); | |
| 470 | 2 | } | |
| 471 | |||
| 472 | } // namespace Svc | ||
| 473 |