GCC Code Coverage Report


Directory: ./
File: FpySequencerValidationState.cpp
Date: 2026-09-03 22:13:45
Exec Total Coverage
Lines: 0 132 0.0%
Functions: 0 7 0.0%
Branches: 0 127 0.0%

Line Branch Exec Source
1 #include <Utils/Hash/Hash.hpp>
2 #include "Svc/FpySequencer/FppConstantsAc.hpp"
3 #include "Svc/FpySequencer/FpySequencer.hpp"
4
5 namespace Svc {
6
7 void FpySequencer::allocateBuffer(FwEnumStoreType identifier, Fw::MemAllocator& allocator, FwSizeType bytes) {
8 // if this assertion fails, you aren't allocating enough bytes for the
9 // FpySequencer. this is because you must have a buffer big enough to fit the
10 // header of a sequence
11 FW_ASSERT(bytes >= Fpy::Header::SERIALIZED_SIZE, static_cast<FwAssertArgType>(bytes));
12 FwSizeType originalBytes = bytes;
13 bool recoverable = false;
14 this->m_allocatorId = identifier;
15 U8* allocatedMemory = static_cast<U8*>(allocator.allocate(identifier, bytes, recoverable));
16 // if this fails, unable to allocate the requested amount of money
17 FW_ASSERT(bytes >= originalBytes, static_cast<FwAssertArgType>(bytes));
18 this->m_sequenceBuffer.setExtBuffer(allocatedMemory, bytes);
19 }
20
21 void FpySequencer::deallocateBuffer(Fw::MemAllocator& allocator) {
22 allocator.deallocate(this->m_allocatorId, this->m_sequenceBuffer.getBuffAddr());
23 this->m_sequenceBuffer.clear();
24 }
25
26 // loads the sequence in memory, and does header/crc/integrity checks.
27 // return SUCCESS if sequence is valid, FAILURE otherwise
28 Fw::Success FpySequencer::validate() {
29 if (this->m_sequenceFilePath.length() == 0) {
30 this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(Os::File::INVALID_ARGUMENT));
31 return Fw::Success::FAILURE;
32 }
33
34 // crc needs to be initialized with a particular value
35 // for the calculation to work
36 this->m_computedCRC.init();
37
38 Os::File sequenceFile;
39 Os::File::Status openStatus = sequenceFile.open(this->m_sequenceFilePath.toChar(), Os::File::OPEN_READ);
40
41 if (openStatus != Os::File::Status::OP_OK) {
42 this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(openStatus));
43 return Fw::Success::FAILURE;
44 }
45
46 Fw::Success readStatus =
47 this->readBytes(sequenceFile, Fpy::Header::SERIALIZED_SIZE, FpySequencer_FileReadStage::HEADER);
48
49 if (readStatus != Fw::Success::SUCCESS) {
50 return Fw::Success::FAILURE;
51 }
52
53 readStatus = this->readHeader();
54
55 if (readStatus != Fw::Success::SUCCESS) {
56 return Fw::Success::FAILURE;
57 }
58
59 readStatus =
60 readBytes(sequenceFile, this->m_sequenceObj.get_header().get_bodySize(), FpySequencer_FileReadStage::BODY);
61
62 if (readStatus != Fw::Success::SUCCESS) {
63 return Fw::Success::FAILURE;
64 }
65
66 readStatus = this->readBody();
67
68 if (readStatus != Fw::Success::SUCCESS) {
69 return Fw::Success::FAILURE;
70 }
71
72 // read footer bytes but don't include in CRC
73 readStatus = this->readBytes(sequenceFile, Fpy::Footer::SERIALIZED_SIZE, FpySequencer_FileReadStage::FOOTER, false);
74
75 if (readStatus != Fw::Success::SUCCESS) {
76 return Fw::Success::FAILURE;
77 }
78
79 readStatus = this->readFooter();
80
81 if (readStatus != Fw::Success::SUCCESS) {
82 return Fw::Success::FAILURE;
83 }
84
85 // make sure we're at EOF. The size() and position() OS calls can fail
86 // on filesystem errors or if the file was concurrently modified by
87 // another FileManager command (e.g. RemoveFile or AppendFile) between
88 // the file open and this point. Treat the failure as a validation
89 // failure rather than aborting the FSW process.
90 FwSizeType sequenceFileSize;
91 Os::File::Status sizeStatus = sequenceFile.size(sequenceFileSize);
92 if (sizeStatus != Os::File::Status::OP_OK) {
93 this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(sizeStatus));
94 return Fw::Success::FAILURE;
95 }
96
97 FwSizeType sequenceFilePosition;
98 Os::File::Status positionStatus = sequenceFile.position(sequenceFilePosition);
99 if (positionStatus != Os::File::Status::OP_OK) {
100 this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(positionStatus));
101 return Fw::Success::FAILURE;
102 }
103
104 if (sequenceFileSize != sequenceFilePosition) {
105 this->log_WARNING_HI_ExtraBytesInSequence(static_cast<FwSizeType>(sequenceFileSize - sequenceFilePosition));
106 return Fw::Success::FAILURE;
107 }
108
109 if (this->m_sequenceArgs.get_size() > Fpy::MAX_STACK_SIZE) {
110 this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(
111 static_cast<Fpy::StackSizeType>(this->m_sequenceArgs.get_size()));
112 return Fw::Success::FAILURE;
113 }
114
115 // The argument size arrives separately from the argument buffer, so it can describe more bytes
116 // than that buffer holds. Reject before anything reads the buffer by that size.
117 const FwSizeType argCapacity = static_cast<FwSizeType>(sizeof(this->m_sequenceArgs.get_buffer()));
118 if (this->m_sequenceArgs.get_size() > argCapacity) {
119 this->log_WARNING_HI_ArgSizeExceedsCapacity(this->m_sequenceArgs.get_size(), argCapacity);
120 return Fw::Success::FAILURE;
121 }
122
123 return Fw::Success::SUCCESS;
124 }
125
126 // reads and validates the header from the m_sequenceBuffer
127 // return SUCCESS if sequence is valid, FAILURE otherwise
128 Fw::Success FpySequencer::readHeader() {
129 // deser header
130 Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_header());
131 if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
132 this->log_WARNING_HI_FileReadDeserializeError(
133 FpySequencer_FileReadStage::HEADER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
134 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
135 return Fw::Success::FAILURE;
136 }
137
138 // check matching schema version
139 if (this->m_sequenceObj.get_header().get_schemaVersion() != Fpy::SCHEMA_VERSION) {
140 this->log_WARNING_HI_WrongSchemaVersion(Fpy::SCHEMA_VERSION,
141 this->m_sequenceObj.get_header().get_schemaVersion());
142 return Fw::Success::FAILURE;
143 }
144
145 if (this->m_sequenceObj.get_header().get_argumentCount() > Fpy::MAX_SEQUENCE_ARG_COUNT) {
146 this->log_WARNING_HI_TooManySequenceArgs(m_sequenceObj.get_header().get_argumentCount(),
147 Fpy::MAX_SEQUENCE_ARG_COUNT);
148 return Fw::Success::FAILURE;
149 }
150
151 if (this->m_sequenceObj.get_header().get_statementCount() > Fpy::MAX_SEQUENCE_STATEMENT_COUNT) {
152 this->log_WARNING_HI_TooManySequenceDirectives(this->m_sequenceObj.get_header().get_statementCount(),
153 Fpy::MAX_SEQUENCE_STATEMENT_COUNT);
154 return Fw::Success::FAILURE;
155 }
156 return Fw::Success::SUCCESS;
157 }
158
159 // reads and validates the body from the m_sequenceBuffer
160 // return SUCCESS if sequence is valid, FAILURE otherwise
161 Fw::Success FpySequencer::readBody() {
162 Fw::SerializeStatus deserStatus;
163
164 const U8 argumentCount = this->m_sequenceObj.get_header().get_argumentCount();
165 this->m_totalExpectedArgSize = 0;
166
167 // deser arguments
168 // Read and deserialize each arg_spec incrementally since they're variable-length
169 for (U8 i = 0; i < argumentCount; i++) {
170 Fpy::ArgSpec& argSpec = this->m_sequenceObj.get_args()[i];
171 deserStatus = this->m_sequenceBuffer.deserializeTo(argSpec);
172 if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
173 this->log_WARNING_HI_FileReadDeserializeError(
174 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
175 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
176 return Fw::Success::FAILURE;
177 }
178
179 m_totalExpectedArgSize += argSpec.get_argSize();
180 }
181
182 // Check for overflow
183 if (m_totalExpectedArgSize > Fpy::MAX_STACK_SIZE) {
184 this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(m_totalExpectedArgSize);
185 return Fw::Success::FAILURE;
186 }
187
188 // Validate total argument size
189 if (this->m_totalExpectedArgSize != this->m_sequenceArgs.get_size()) {
190 this->log_WARNING_HI_ArgSizeMismatch(this->m_totalExpectedArgSize, this->m_sequenceArgs.get_size(),
191 this->m_sequenceFilePath);
192 return Fw::Success::FAILURE;
193 }
194
195 // deser statements
196 const U16 statementCount = this->m_sequenceObj.get_header().get_statementCount();
197 for (U16 statementIdx = 0; statementIdx < statementCount; statementIdx++) {
198 // deser statement
199 deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_statements()[statementIdx]);
200 if (deserStatus != Fw::FW_SERIALIZE_OK) {
201 this->log_WARNING_HI_FileReadDeserializeError(
202 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
203 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
204 return Fw::Success::FAILURE;
205 }
206 }
207 return Fw::Success::SUCCESS;
208 }
209
210 // reads and validates the footer from the m_sequenceBuffer
211 // return SUCCESS if sequence is valid, FAILURE otherwise
212 Fw::Success FpySequencer::readFooter() {
213 Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_footer());
214 if (deserStatus != Fw::FW_SERIALIZE_OK) {
215 this->log_WARNING_HI_FileReadDeserializeError(
216 FpySequencer_FileReadStage::FOOTER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
217 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
218 return Fw::Success::FAILURE;
219 }
220
221 // need this for some reason to "finalize" the crc TODO get an explanation on this
222 U32 computedCRC = 0;
223 this->m_computedCRC.finalize(computedCRC);
224
225 if (computedCRC != this->m_sequenceObj.get_footer().get_crc()) {
226 this->log_WARNING_HI_WrongCRC(this->m_sequenceObj.get_footer().get_crc(), computedCRC);
227 return Fw::Success::FAILURE;
228 }
229
230 return Fw::Success::SUCCESS;
231 }
232
233 // reads some bytes from the open file into the m_sequenceBuffer.
234 // return success if successful
235 Fw::Success FpySequencer::readBytes(Os::File& file,
236 FwSizeType expectedReadLen,
237 const FpySequencer_FileReadStage& readStage,
238 bool updateCrc) {
239 FW_ASSERT(file.isOpen());
240 // this has to be declared a var because file.read must take a ref
241 FwSizeType actualReadLen = expectedReadLen;
242
243 const FwSizeType capacity = this->m_sequenceBuffer.getCapacity();
244
245 // if this fails, then you need to give the sequencer more buffer memory. pass in a bigger number
246 // to fpySeq.allocateBuffer(). This is usually done in topology setup CPP
247 if (expectedReadLen > capacity) {
248 this->log_WARNING_HI_InsufficientBufferSpace(static_cast<U64>(capacity), this->m_sequenceFilePath);
249 return Fw::Success::FAILURE;
250 }
251
252 Os::File::Status fileStatus = file.read(this->m_sequenceBuffer.getBuffAddr(), actualReadLen);
253
254 if (fileStatus != Os::File::OP_OK) {
255 this->log_WARNING_HI_FileReadError(readStage, this->m_sequenceFilePath, static_cast<I32>(fileStatus));
256 return Fw::Success::FAILURE;
257 }
258
259 if (actualReadLen < expectedReadLen) {
260 this->log_WARNING_HI_EndOfFileError(readStage, this->m_sequenceFilePath);
261 return Fw::Success::FAILURE;
262 }
263
264 // should probably fail if we read in MORE bytes than we ask for
265 FW_ASSERT(expectedReadLen == actualReadLen, static_cast<FwAssertArgType>(expectedReadLen),
266 static_cast<FwAssertArgType>(actualReadLen));
267
268 Fw::SerializeStatus serializeStatus =
269 this->m_sequenceBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(expectedReadLen));
270 FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
271
272 if (updateCrc) {
273 this->m_computedCRC.update(this->m_sequenceBuffer.getBuffAddr(), expectedReadLen);
274 }
275
276 return Fw::Success::SUCCESS;
277 }
278
279 } // namespace Svc
280