Skip to content

Commit 85d4717

Browse files
committed
pldmtool: Add file I/O and checksum options to mctpRaw
Enhance the `mctpRaw` subcommand with options to read request data from a file (`--file-in`), write response data to a file (`--file-out`), and append or validate checksum (`--checksum`). These additions improve usability for large payloads and ensure data integrity during raw MCTP transactions. Contents of the file specified in option --file-in are appended to the contents of the option --data. pldmtool mctpRaw --help send an MCTP raw request and print response Usage: pldmtool mctpRaw [OPTIONS] Options: -h,--help Print this help message and exit -m,--mctp_eid UINT MCTP endpoint ID -v,--verbose -n,--retry-count UINT Number of retry when PLDM request message is failed -e,--network-id UINT REQUIRED MCTP NetworkId -d,--data UINT ... REQUIRED raw MCTP data -i,--file-in TEXT Read in the file to be sent -o,--file-out TEXT Write out the received file -c,--checksum Append/Validate checksum -p,--prealloc-tag use pre-allocated MCTP tag for this request Example: pldmtool mctpRaw -m 21 -e 1 --data 0x7F 0x00 0x00 0x0E 0x78 0x80 0x02 --file-in req.bin --file-out resp.bin --checksum Upstream-status: Inappropriate Signed-off-by: Shirish Pargaonkar <Shirish.Pargaonkar@amd.com>
1 parent 92694a1 commit 85d4717

File tree

1 file changed

+232
-9
lines changed

1 file changed

+232
-9
lines changed

pldmtool/pldmtool.cpp

Lines changed: 232 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ namespace
7272
std::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+
75100
class 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+
110333
void registerCommand(CLI::App& app)
111334
{
112335
auto mctpRaw = app.add_subcommand(

0 commit comments

Comments
 (0)