Skip to content

Commit a017b64

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 a017b64

File tree

1 file changed

+217
-9
lines changed

1 file changed

+217
-9
lines changed

pldmtool/pldmtool.cpp

Lines changed: 217 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ 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 = 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+
7599
class 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+
110318
void registerCommand(CLI::App& app)
111319
{
112320
auto mctpRaw = app.add_subcommand(

0 commit comments

Comments
 (0)