@@ -72,6 +72,30 @@ 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 = 8 ;
94+ inline constexpr unsigned int FSFIELD = 4 ;
95+ inline constexpr unsigned int CCODEBYTE = 6 ;
96+ inline constexpr unsigned int MAXFILESIZE = 0x4000 ;
97+ inline constexpr unsigned int AMD_IANA_NUM = 0xe78 ;
98+
7599class MctpRawOp : public CommandInterface
76100{
77101 public:
@@ -90,23 +114,207 @@ class MctpRawOp : public CommandInterface
90114 app->add_option (" -d,--data" , rawData, " raw MCTP data" )
91115 ->required ()
92116 ->expected (-3 );
117+ app->add_option (" -i,--file-in " , inFileName, " Read in the file to be sent" );
118+ app->add_option (" -o,--file-out" , outFileName, " Write out the received file" );
119+ app->add_flag (" -c,--checksum" , checksum, " Append/Validate checksum" );
93120 app->add_flag (" -p,--prealloc-tag" , mctpPreAllocTag,
94121 " use pre-allocated MCTP tag for this request" );
95122 }
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- {}
105123
124+ const std::string getInFileName () const {return inFileName;}
125+ const std::string getOutFileName () const {return outFileName;}
126+ std::pair<int , std::vector<uint8_t >> createRequestMsg () override ;
127+ void parseResponseMsg (pldm_msg *, size_t ) override ;
106128 private:
107129 std::vector<uint8_t > rawData;
130+ std::string outFileName{};
131+ std::string inFileName{};
132+ bool checksum{false };
133+
134+ void appendInfile ();
108135};
109136
137+ /*
138+ * @brief Append an input firmware file to the rawData buffer for transmission.
139+ *
140+ * This function:
141+ * 1. Opens the input file specified by getInFileName().
142+ * 2. Reads the entire file into memory.
143+ * 3. Appends the file-size field (big-endian) to rawData.
144+ * 4. Appends the file contents to rawData.
145+ * 5. Clears the IC (Integrity Check) bit in the header, and if checksum
146+ * is enabled, sets it and appends a CRC32 checksum of the file.
147+ *
148+ * All errors (file open/read failures) are printed and cause an early return.
149+ */
150+
151+ void MctpRawOp::appendInfile ()
152+ {
153+ std::ifstream toDevice;
154+
155+ try {
156+ toDevice.open (getInFileName (), std::ios::in | std::ios::binary | std::ios::ate);
157+ if (!toDevice)
158+ throw std::runtime_error (" Error opening file: " + getInFileName ());
159+ }
160+
161+ catch (const std::runtime_error & e) {
162+ std::cout << e.what () << std::endl;
163+ return ;
164+ }
165+
166+ std::streamsize fSize = toDevice.tellg (); // size of the file to be sent
167+ toDevice.seekg (0 );
168+
169+ std::vector<uint8_t > fData (fSize ); // for reading in the entire file
170+
171+ try {
172+ toDevice.read (reinterpret_cast <char *>(fData .data ()), fSize );
173+ if (!toDevice)
174+ throw std::runtime_error (" Error reading file: " + getInFileName ());
175+ }
176+
177+ catch (const std::runtime_error & e) {
178+ std::cout << e.what () << std::endl;
179+ return ;
180+ }
181+
182+ uint32_t fsField{static_cast <uint32_t >(fSize )}; // size of the file
183+ if constexpr (std::endian::native == std::endian::little)
184+ fsField = std::byteswap (fsField);
185+ const uint8_t * fsValue = reinterpret_cast <const uint8_t *>(&fsField);
186+ rawData.insert (rawData.end (), fsValue, fsValue + sizeof (fsField));
187+
188+ rawData.insert (rawData.end (), fData .begin (), fData .end ());
189+
190+ rawData[0 ] &= 0x7F ; // clear the IC bit (bit 7 [0:7]) e.g. in case 0xFF, to 0x7F
191+ if (checksum) {
192+ rawData[0 ] |= 0x80 ; // set the IC bit (bit 7 [0:7]) e.g. 0x7F to 0xFF
193+
194+ uint32_t csField = pldm_edac_crc32 (fData .data (), fData .size ());
195+ if constexpr (std::endian::native == std::endian::little)
196+ csField = std::byteswap (csField);
197+ const uint8_t * csValue = reinterpret_cast <const uint8_t *>(&csField);
198+ rawData.insert (rawData.end (), csValue, csValue + sizeof (csField));
199+ }
200+ }
201+
202+ std::pair<int , std::vector<uint8_t >> MctpRawOp::createRequestMsg ()
203+ {
204+ if (!getInFileName ().empty ()) // only for the -i option
205+ appendInfile ();
206+
207+ return {PLDM_SUCCESS, rawData};
208+ }
209+
210+ /*
211+ * @brief Parse and process a PLDM response message received over MCTP.
212+ *
213+ * This function validates and extracts payload information from a raw PLDM
214+ * response message. The following operations are performed:
215+ *
216+ * 1. Validate input pointer and minimum message size.
217+ * 2. Check the PLDM completion code and abort on non-zero values.
218+ * 3. Verify the 4-byte IANA number (big-endian) against the expected AMD
219+ * identifier.
220+ * 4. Extract and validate the reported firmware file size.
221+ * 5. If an output filename is specified, extract the firmware payload data.
222+ * 6. If checksum validation is enabled, compute CRC32 of the payload and
223+ * validate it against the received checksum.
224+ * 7. Write the received firmware data to the specified output file.
225+ *
226+ * Errors and validation failures are reported to stderr and result in an
227+ * immediate return without modifying any output file.
228+ *
229+ * @param pmsg Pointer to the raw PLDM message buffer.
230+ * @param size Total size of the PLDM message in bytes.
231+ *
232+ * @note The function expects the message to contain AMD-specific vendor data
233+ * with fields in big-endian format as defined by the device protocol.
234+ */
235+
236+ void MctpRawOp::parseResponseMsg (pldm_msg *pmsg, size_t size)
237+ {
238+ if (!pmsg)
239+ return ;
240+
241+ if (size > DEVHDRSIZE) {
242+ const uint8_t * msg{reinterpret_cast <uint8_t *>(pmsg)};
243+
244+ // Check Completion Code (byte)
245+ if (msg[CCODEBYTE] != 0 ) {
246+ std::cerr << " Non-zero completion code: " << msg[CCODEBYTE] << std::endl;
247+ return ;
248+ }
249+
250+ // Extract four bytes of IANA number to verify
251+ uint32_t iana;
252+ memcpy (&iana, msg, sizeof (iana)); // IANA number in big endian format
253+ if constexpr (std::endian::native == std::endian::little)
254+ iana = std::byteswap (iana);
255+ if (iana != AMD_IANA_NUM) {
256+ std::cerr << std::showbase << std::hex << " Received IANA number: " << iana << " does not match with: " << AMD_IANA_NUM << std::endl;
257+ return ;
258+ }
259+
260+ // Extract four bytes to obtain the received file size
261+ uint32_t fSize ;
262+ memcpy (&fSize , msg + DEVHDRSIZE, sizeof (fSize )); // File size is in Big endian format as per spec
263+ if constexpr (std::endian::native == std::endian::little)
264+ fSize = std::byteswap (fSize );
265+ try {
266+ if (fSize > MAXFILESIZE)
267+ throw std::runtime_error (" Invalid file size in bytes: " + std::to_string (fSize ));
268+ }
269+
270+ catch (const std::runtime_error & e) {
271+ std::cout << e.what () << std::endl;
272+ return ;
273+ }
274+
275+ // Request for GETFWVERS without specifying option -o
276+ if (getOutFileName ().empty ())
277+ return ;
278+
279+ std::vector<uint8_t > fData {msg + DEVHDRSIZE + FSFIELD, msg + DEVHDRSIZE + FSFIELD + fSize };
280+
281+ if (checksum) {
282+ // Calculate checksum
283+ const uint32_t crc = pldm_edac_crc32 (fData .data (), fData .size ());
284+
285+ // Extract four bytes of checksum
286+ uint32_t checkSum;
287+ memcpy (&checkSum, msg + DEVHDRSIZE + FSFIELD + fSize , sizeof (checkSum)); // Checksum is in Big endian format as per spec
288+ if constexpr (std::endian::native == std::endian::little)
289+ checkSum = std::byteswap (checkSum);
290+
291+ if (crc != checkSum) {
292+ std::cerr << std::showbase << std::hex << " CRC: " << crc << " does not match the received CRC: " << checkSum << std::endl;
293+ return ;
294+ }
295+ }
296+
297+ std::ofstream fromFile;
298+
299+ try {
300+ std::ofstream fromFile (getOutFileName (), std::ios::out | std::ios::binary);
301+
302+ if (!fromFile.is_open ()) {
303+ throw std::runtime_error (" Error opening file: " + getOutFileName ());
304+ }
305+
306+ fromFile.write (reinterpret_cast <const char *>(fData .data ()), fData .size ());
307+ if (!fromFile) {
308+ throw std::runtime_error (" Error writing to file: " + getOutFileName ());
309+ }
310+ }
311+
312+ catch (const std::exception& e) {
313+ std::cerr << e.what () << ' \n ' ;
314+ }
315+ }
316+ }
317+
110318void registerCommand (CLI::App& app)
111319{
112320 auto mctpRaw = app.add_subcommand (
0 commit comments