@@ -72,6 +72,31 @@ namespace
7272std::vector<std::unique_ptr<CommandInterface>> commands;
7373}
7474
75+ /*
76+ * • The file size and CRC32 fields are always stored in **big-endian**
77+ * format regardless of host endianness.
78+ *
79+ * • If checksum is disabled, the IC bit is cleared and the CRC32 field
80+ * is omitted.
81+ *
82+ * • rawData[] is assembled in this exact order:
83+ *
84+ * rawData = [Header] + [File Size] + [File Data] + [Optional CRC]
85+ *
86+ * • The offsets DEVHDRSIZE and FSFIELD refer to the start of the file-size
87+ * field and help determine where payload sections begin.
88+ *
89+ * • Header include AMD IANA number in big-endian format
90+ *
91+ */
92+
93+ inline constexpr unsigned int DEVHDRSIZE = 7 ;
94+ inline constexpr unsigned int FSFIELD = 4 ;
95+ inline constexpr unsigned int CSFIELD = 4 ;
96+ inline constexpr unsigned int CCODEBYTE = 6 ;
97+ inline constexpr unsigned int MAXFILESIZE = 0x4000 ;
98+ inline constexpr unsigned int AMD_IANA_NUM = 0xe78 ;
99+
75100class MctpRawOp : public CommandInterface
76101{
77102 public:
@@ -90,23 +115,221 @@ class MctpRawOp : public CommandInterface
90115 app->add_option (" -d,--data" , rawData, " raw MCTP data" )
91116 ->required ()
92117 ->expected (-3 );
118+ app->add_option (" -i,--file-in " , inFileName, " Read in the file to be sent" );
119+ app->add_option (" -o,--file-out" , outFileName, " Write out the received file" );
120+ app->add_flag (" -c,--checksum" , checksum, " Append/Validate checksum" );
93121 app->add_flag (" -p,--prealloc-tag" , mctpPreAllocTag,
94122 " use pre-allocated MCTP tag for this request" );
95123 }
96- std::pair<int , std::vector<uint8_t >> createRequestMsg () override
97-
98- {
99- return {PLDM_SUCCESS, rawData};
100- }
101-
102- void parseResponseMsg (pldm_msg* /* responsePtr */ ,
103- size_t /* payloadLength */ ) override
104- {}
105124
125+ const std::string getInFileName () const {return inFileName;}
126+ const std::string getOutFileName () const {return outFileName;}
127+ std::pair<int , std::vector<uint8_t >> createRequestMsg () override ;
128+ void parseResponseMsg (pldm_msg *, size_t ) override ;
106129 private:
107130 std::vector<uint8_t > rawData;
131+ std::string outFileName{};
132+ std::string inFileName{};
133+ bool checksum{false };
134+
135+ void appendInfile ();
108136};
109137
138+ /*
139+ * @brief Append an input firmware file to the rawData buffer for transmission.
140+ *
141+ * This function:
142+ * 1. Opens the input file specified by getInFileName().
143+ * 2. Reads the entire file into memory.
144+ * 3. Appends the file-size field (big-endian) to rawData.
145+ * 4. Appends the file contents to rawData.
146+ * 5. Clears the IC (Integrity Check) bit in the header, and if checksum
147+ * is enabled, sets it and appends a CRC32 checksum of the file.
148+ *
149+ * All errors (file open/read failures) are printed and cause an early return.
150+ */
151+
152+ void MctpRawOp::appendInfile ()
153+ {
154+ std::ifstream toDevice;
155+
156+ try {
157+ toDevice.open (getInFileName (), std::ios::in | std::ios::binary | std::ios::ate);
158+ if (!toDevice)
159+ throw std::runtime_error (" Error opening file: " + getInFileName ());
160+ }
161+
162+ catch (const std::runtime_error & e) {
163+ std::cout << e.what () << std::endl;
164+ return ;
165+ }
166+
167+ std::streamsize fSize = toDevice.tellg (); // size of the file to be sent
168+ toDevice.seekg (0 );
169+
170+ std::vector<uint8_t > fData (fSize ); // for reading in the entire file
171+
172+ try {
173+ toDevice.read (reinterpret_cast <char *>(fData .data ()), fSize );
174+ if (!toDevice)
175+ throw std::runtime_error (" Error reading file: " + getInFileName ());
176+ }
177+
178+ catch (const std::runtime_error & e) {
179+ std::cout << e.what () << std::endl;
180+ return ;
181+ }
182+
183+ uint32_t fsField{static_cast <uint32_t >(fSize )}; // size of the file
184+ if constexpr (std::endian::native == std::endian::little)
185+ fsField = std::byteswap (fsField);
186+ const uint8_t * fsValue = reinterpret_cast <const uint8_t *>(&fsField);
187+ rawData.insert (rawData.end (), fsValue, fsValue + sizeof (fsField));
188+
189+ rawData.insert (rawData.end (), fData .begin (), fData .end ());
190+
191+ rawData[0 ] &= 0x7F ; // clear the IC bit (bit 7 [0:7]) e.g. in case 0xFF, to 0x7F
192+ if (checksum) {
193+ rawData[0 ] |= 0x80 ; // set the IC bit (bit 7 [0:7]) e.g. 0x7F to 0xFF
194+
195+ uint32_t csField = pldm_edac_crc32 (fData .data (), fData .size ());
196+ if constexpr (std::endian::native == std::endian::little)
197+ csField = std::byteswap (csField);
198+ const uint8_t * csValue = reinterpret_cast <const uint8_t *>(&csField);
199+ rawData.insert (rawData.end (), csValue, csValue + sizeof (csField));
200+ }
201+ }
202+
203+ std::pair<int , std::vector<uint8_t >> MctpRawOp::createRequestMsg ()
204+ {
205+ if (!getInFileName ().empty ()) // only for the -i option
206+ appendInfile ();
207+
208+ return {PLDM_SUCCESS, rawData};
209+ }
210+
211+ /*
212+ * @brief Parse and process a PLDM response message received over MCTP.
213+ *
214+ * This function validates and extracts payload information from a raw PLDM
215+ * response message. The following operations are performed:
216+ *
217+ * 1. Validate input pointer and minimum message size.
218+ * 2. Check the PLDM completion code and abort on non-zero values.
219+ * 3. Verify the 4-byte IANA number (big-endian) against the expected AMD
220+ * identifier.
221+ * 4. Extract and validate the reported firmware file size.
222+ * 5. If an output filename is specified, extract the firmware payload data.
223+ * 6. If checksum validation is enabled, compute CRC32 of the payload and
224+ * validate it against the received checksum.
225+ * 7. Write the received firmware data to the specified output file.
226+ *
227+ * Errors and validation failures are reported to stderr and result in an
228+ * immediate return without modifying any output file.
229+ *
230+ * @param pmsg Pointer to the raw PLDM message buffer.
231+ * @param size Total size of the PLDM message in bytes.
232+ *
233+ * @note The function expects the message to contain AMD-specific vendor data
234+ * with fields in big-endian format as defined by the device protocol.
235+ */
236+
237+ void MctpRawOp::parseResponseMsg (pldm_msg *pmsg, size_t size)
238+ {
239+ if (!pmsg)
240+ return ;
241+
242+ if (size < DEVHDRSIZE)
243+ return ;
244+
245+ const uint8_t * msg{reinterpret_cast <uint8_t *>(pmsg)};
246+
247+ // Extract four bytes of IANA number to verify
248+ uint32_t iana;
249+ memcpy (&iana, msg, sizeof (iana)); // IANA number in big endian format
250+ if constexpr (std::endian::native == std::endian::little)
251+ iana = std::byteswap (iana);
252+ if (iana != AMD_IANA_NUM) {
253+ std::cerr << std::showbase << std::hex << " Received IANA number: " << iana << " does not match with: " << AMD_IANA_NUM << std::endl;
254+ return ;
255+ }
256+
257+ // Check Completion Code (byte)
258+ if (msg[CCODEBYTE] != 0 ) {
259+ std::cerr << " Non-zero completion code: " << msg[CCODEBYTE] << std::endl;
260+ return ;
261+ }
262+
263+ if (size < (DEVHDRSIZE + FSFIELD))
264+ return ;
265+
266+ // Extract four bytes to obtain the received file size
267+ uint32_t fSize ;
268+ memcpy (&fSize , msg + DEVHDRSIZE, sizeof (fSize )); // File size is in Big endian format as per spec
269+ if constexpr (std::endian::native == std::endian::little)
270+ fSize = std::byteswap (fSize );
271+ try {
272+ if (fSize > MAXFILESIZE)
273+ throw std::runtime_error (" Invalid file size in bytes: " + std::to_string (fSize ));
274+ }
275+
276+ catch (const std::runtime_error & e) {
277+ std::cout << e.what () << std::endl;
278+ return ;
279+ }
280+
281+ // If -o option is not specified, nothing to write to
282+ if (getOutFileName ().empty ())
283+ return ;
284+
285+ if (size < (DEVHDRSIZE + FSFIELD + fSize )) {
286+ std::cerr << " Received response of " << size << " bytes shorter than expected size of " << ((DEVHDRSIZE + FSFIELD + fSize )) << std::endl;
287+ return ;
288+ }
289+
290+ std::vector<uint8_t > fData {msg + DEVHDRSIZE + FSFIELD, msg + DEVHDRSIZE + FSFIELD + fSize };
291+
292+ if (checksum) {
293+ if (size < (DEVHDRSIZE + FSFIELD + fSize + CSFIELD)) {
294+ std::cerr << " Received response of " << size << " bytes does not contain checksum" << std::endl;
295+ return ;
296+ }
297+
298+ // Calculate checksum
299+ const uint32_t crc = pldm_edac_crc32 (fData .data (), fData .size ());
300+
301+ // Extract four bytes of checksum
302+ uint32_t checkSum;
303+ memcpy (&checkSum, msg + DEVHDRSIZE + FSFIELD + fSize , sizeof (checkSum)); // Checksum is in Big endian format as per spec
304+ if constexpr (std::endian::native == std::endian::little)
305+ checkSum = std::byteswap (checkSum);
306+
307+ if (crc != checkSum) {
308+ std::cerr << std::showbase << std::hex << " CRC: " << crc << " does not match the received CRC: " << checkSum << std::endl;
309+ return ;
310+ }
311+ }
312+
313+ std::ofstream fromFile;
314+
315+ try {
316+ std::ofstream fromFile (getOutFileName (), std::ios::out | std::ios::binary);
317+
318+ if (!fromFile.is_open ()) {
319+ throw std::runtime_error (" Error opening file: " + getOutFileName ());
320+ }
321+
322+ fromFile.write (reinterpret_cast <const char *>(fData .data ()), fData .size ());
323+ if (!fromFile) {
324+ throw std::runtime_error (" Error writing to file: " + getOutFileName ());
325+ }
326+ }
327+
328+ catch (const std::exception& e) {
329+ std::cerr << e.what () << ' \n ' ;
330+ }
331+ }
332+
110333void registerCommand (CLI::App& app)
111334{
112335 auto mctpRaw = app.add_subcommand (
0 commit comments