1use log::{debug, error};
12
13use thiserror::Error;
14
15use nom::{
16 combinator::{all_consuming, map},
17 multi::length_value,
18 number::complete::le_u32,
19 sequence::tuple,
20 IResult,
21};
22
23use pldm::PldmError;
24
25use crate::pkg;
26use crate::{
27 DeviceIdentifiers, FirmwareParameters, FwCode, GetStatusResponse,
28 PldmFDState, RequestUpdateResponse, UpdateComponentResponse,
29 UpdateTransferProgress, PLDM_TYPE_FW,
30};
31
32pub type Result<T> = core::result::Result<T, PldmUpdateError>;
33
34#[derive(Error, Debug)]
35pub enum PldmUpdateError {
36 #[error("PLDM error: {0}")]
37 Pldm(#[from] PldmError),
38 #[error("PLDM protocol error: {0}")]
39 Protocol(String),
40 #[error("PLDM command (0x{0:02x}) failed with 0x{1:02x}")]
41 Command(u8, u8),
42 #[error("PLDM Update error: {0}")]
43 Update(String),
44 #[error("PLDM Package error: {0}")]
45 Package(#[from] pkg::PldmPackageError),
46 }
49
50impl PldmUpdateError {
51 fn new_command(cmd: u8, cc: u8) -> Self {
52 Self::Command(cmd, cc)
53 }
54
55 fn new_proto(desc: String) -> Self {
56 Self::Protocol(desc)
57 }
58
59 fn new_update(desc: String) -> Self {
60 Self::Update(desc)
61 }
62}
63
64#[derive(Debug)]
65pub struct Update {
66 pub package: pkg::Package,
67 pub index: u8,
68 pub components: Vec<usize>,
69}
70
71impl Update {
72 pub fn new(
73 dev: &DeviceIdentifiers,
74 _fwp: &FirmwareParameters,
75 pkg: pkg::Package,
76 index: Option<u8>,
77 force_device: Option<usize>,
78 force_components: Vec<usize>,
79 ) -> Result<Self> {
80 let dev = match force_device {
81 Some(n) => {
82 if n >= pkg.devices.len() {
83 return Err(PldmUpdateError::new_update(
84 "invalid device index".into(),
85 ));
86 }
87 &pkg.devices[n]
88 }
89 None => {
90 let fwdevs = pkg
91 .devices
92 .iter()
93 .filter(|d| &d.ids == dev)
94 .collect::<Vec<_>>();
95
96 if fwdevs.is_empty() {
97 return Err(PldmUpdateError::new_update(
98 "no matching devices".into(),
99 ));
100 }
101
102 if fwdevs.len() != 1 {
103 return Err(PldmUpdateError::new_update(
104 "multiple matching devices".into(),
105 ));
106 }
107
108 *fwdevs.first().unwrap()
109 }
110 };
111
112 let index = index.unwrap_or(0u8);
113
114 let components = if !force_components.is_empty() {
115 if force_components.iter().any(|c| c >= &pkg.components.len()) {
116 return Err(PldmUpdateError::new_update(
117 "invalid components".into(),
118 ));
119 }
120
121 force_components
122 } else {
123 dev.components.as_index_vec()
124 };
125
126 Ok(Self {
127 package: pkg,
128 components,
129 index,
130 })
131 }
132}
133
134pub fn query_device_identifiers(
135 comm: &mut impl mctp::ReqChannel,
136) -> Result<DeviceIdentifiers> {
137 let req = pldm::PldmRequest::new(PLDM_TYPE_FW, 0x01);
138
139 let rsp = pldm::pldm_xfer(comm, req)?;
140
141 if rsp.cc != 0 {
142 return Err(PldmUpdateError::new_command(0x01, rsp.cc));
143 }
144
145 let f = length_value(map(le_u32, |l| l + 1), DeviceIdentifiers::parse);
146
147 let res = all_consuming(f)(&rsp.data);
148
149 res.map(|(_, d)| d).map_err(|_e| {
150 PldmUpdateError::new_proto("can't parse QDI response".into())
151 })
152}
153
154pub fn query_firmware_parameters(
155 comm: &mut impl mctp::ReqChannel,
156) -> Result<FirmwareParameters<'_>> {
157 let req = pldm::PldmRequest::new(PLDM_TYPE_FW, 0x02);
158
159 let rsp = pldm::pldm_xfer(comm, req)?;
160
161 if rsp.cc != 0 {
162 return Err(PldmUpdateError::new_command(0x02, rsp.cc));
163 }
164
165 let f = FirmwareParameters::parse;
166
167 let res = all_consuming(f)(&rsp.data);
168
169 res.map(|(_, d)| d).map_err(|_e| {
170 PldmUpdateError::new_proto("can't parse QFP response".into())
171 })
172}
173
174const XFER_SIZE: usize = 16 * 1024;
175
176pub fn request_update(
177 comm: &mut impl mctp::ReqChannel,
178 update: &Update,
179) -> Result<RequestUpdateResponse> {
180 check_fd_state(comm, PldmFDState::Idle)?;
181
182 let sz = XFER_SIZE as u32;
183 let mut data = vec![];
184 data.extend_from_slice(&sz.to_le_bytes());
185 data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&1u8.to_le_bytes()); data.extend_from_slice(&0u16.to_le_bytes()); update.package.version.write_utf8_bytes(&mut data);
189
190 let req = pldm::PldmRequest::new_data(PLDM_TYPE_FW, 0x10, data);
191 let rsp = pldm::pldm_xfer(comm, req)?;
192
193 if rsp.cc != 0 {
194 return Err(PldmUpdateError::new_command(0x10, rsp.cc));
195 }
196
197 let res = all_consuming(RequestUpdateResponse::parse)(&rsp.data);
198
199 res.map(|(_, d)| d).map_err(|_e| {
200 PldmUpdateError::new_proto("can't parse RU response".into())
201 })
202}
203
204pub fn cancel_update(comm: &mut impl mctp::ReqChannel) -> Result<()> {
205 let req = pldm::PldmRequest::new(PLDM_TYPE_FW, 0x1d);
206 let rsp = pldm::pldm_xfer(comm, req)?;
207 debug!("cancel rsp: cc {:x}, data {:?}", rsp.cc, rsp.data);
208 Ok(())
209}
210
211pub fn update_component(
212 comm: &mut impl mctp::ReqChannel,
213 listener: &mut impl mctp::Listener,
214 package: &pkg::Package,
215 component: &pkg::PackageComponent,
216 index: u8,
217) -> Result<()> {
218 update_component_progress(comm, listener, package, component, index, |_| ())
219}
220
221pub fn pass_component_table(
222 comm: &mut impl mctp::ReqChannel,
223 update: &Update,
224) -> Result<()> {
225 let components = &update.components;
226 let len = components.len();
227
228 check_fd_state(comm, PldmFDState::LearnComponents)?;
229
230 for (n, idx) in components.iter().enumerate() {
231 let component = update.package.components.get(*idx).unwrap();
232
233 let mut data = vec![];
234 data.push(xfer_flags(n, len));
235 let c = u16::from(&component.classification);
236 data.extend_from_slice(&c.to_le_bytes());
237 data.extend_from_slice(&component.identifier.to_le_bytes());
238
239 data.extend_from_slice(&update.index.to_le_bytes());
240
241 data.extend_from_slice(&component.comparison_stamp.to_le_bytes());
242
243 component.version.write_utf8_bytes(&mut data);
244
245 let req = pldm::PldmRequest::new_data(PLDM_TYPE_FW, 0x13, data);
246 let rsp = pldm::pldm_xfer(comm, req)?;
247
248 if rsp.cc != 0 {
249 return Err(PldmUpdateError::new_command(0x13, rsp.cc));
250 }
251
252 if rsp.data.len() < 2 {
253 return Err(PldmUpdateError::new_proto(
254 "Invalid PCT response".into(),
255 ));
256 }
257
258 if rsp.data[0] != 0 {
259 match rsp.data[1] {
260 0x00 => (),
261 0x06 => {
262 return Err(PldmUpdateError::new_update(format!(
263 "unsupported component {}",
264 rsp.data[1]
265 )))
266 }
267 x => {
268 return Err(PldmUpdateError::new_proto(format!(
269 "unknown PCT response {:02x}",
270 x
271 )))
272 }
273 }
274 }
275 }
276
277 Ok(())
278}
279
280fn xfer_flags(idx: usize, len: usize) -> u8 {
281 let mut xfer_flags: u8 = 0x0;
282 if idx == 0 {
283 xfer_flags |= 0x1;
284 }
285 if idx == len - 1 {
286 xfer_flags |= 0x4;
287 }
288 if xfer_flags == 0 {
289 xfer_flags = 0x2;
290 }
291 xfer_flags
292}
293
294pub fn update_component_progress<F>(
295 comm: &mut impl mctp::ReqChannel,
296 listener: &mut impl mctp::Listener,
297 package: &pkg::Package,
298 component: &pkg::PackageComponent,
299 index: u8,
300 mut progress: F,
301) -> Result<()>
302where
303 F: FnMut(&UpdateTransferProgress),
304{
305 check_fd_state(comm, PldmFDState::ReadyXfer)?;
306
307 let mut data = vec![];
308 let c = u16::from(&component.classification);
309 data.extend_from_slice(&c.to_le_bytes());
310 data.extend_from_slice(&component.identifier.to_le_bytes());
311
312 data.extend_from_slice(&index.to_le_bytes());
313
314 data.extend_from_slice(&component.comparison_stamp.to_le_bytes());
315
316 let sz: u32 = component.file_size as u32;
317 let mut sz_done: u32 = 0;
318 data.extend_from_slice(&sz.to_le_bytes());
319
320 data.extend_from_slice(&0u32.to_le_bytes());
322
323 component.version.write_utf8_bytes(&mut data);
324
325 let req = pldm::PldmRequest::new_data(PLDM_TYPE_FW, 0x14, data);
326 let rsp = pldm::pldm_xfer(comm, req)?;
327
328 if rsp.cc != 0 {
329 return Err(PldmUpdateError::new_command(0x14, rsp.cc));
330 }
331
332 let (_, res) =
333 all_consuming(UpdateComponentResponse::parse)(rsp.data.as_ref())
334 .map_err(|_e| {
335 PldmUpdateError::new_proto(
336 "can't parse Update Component response".into(),
337 )
338 })?;
339
340 if res.response_code != 0 {
341 return Err(PldmUpdateError::new_update(format!(
342 "Update Component rejected with code 0x{:02x}",
343 res.response_code
344 )));
345 }
346
347 let start = chrono::Utc::now();
348
349 loop {
350 let (fw_req, mut req_ep) = pldm::pldm_rx_req(listener)?;
352
353 if fw_req.typ != PLDM_TYPE_FW {
354 return Err(PldmUpdateError::new_proto(format!(
355 "unexpected type during update: {fw_req:?}"
356 )));
357 }
358
359 match fw_req.cmd {
360 0x15 => {
361 let res: IResult<_, _> = all_consuming(tuple((le_u32, le_u32)))(
363 fw_req.data.as_ref(),
364 );
365
366 let (_, (offset, len)) = res.map_err(|_e| {
367 PldmUpdateError::new_proto("RFD parse error".into())
368 })?;
369
370 let mut buf = vec![0u8; len as usize];
371
372 package.read_component(component, offset, &mut buf)?;
373
374 let mut fw_resp = fw_req.response();
375
376 fw_resp.cc = 0;
377 fw_resp.set_data(buf);
378
379 pldm::pldm_tx_resp(&mut req_ep, &fw_resp)?;
380
381 sz_done += len;
382 let elapsed = chrono::Utc::now() - start;
383
384 let bps;
385 let remaining;
386 if elapsed.is_zero() || sz_done == 0 {
387 bps = 0.;
388 remaining = chrono::TimeDelta::days(1);
389 } else {
390 let rate = elapsed / sz_done as i32; let sz_left = if sz_done <= sz { sz - sz_done } else { 0 };
394
395 remaining = rate * sz_left as i32;
396 bps = 1_000_000.0 / rate.num_microseconds().unwrap() as f32;
398 }
399 let percent = ((100 * (sz_done as u64)) / sz as u64) as u8;
400
401 let u = UpdateTransferProgress {
402 cur_xfer: Some((offset, len)),
403 percent,
404 bps,
405 remaining,
406 duration: elapsed,
407 complete: false,
408 };
409
410 progress(&u);
411 }
412 0x16 => {
413 let res = fw_req.data[0];
415 let elapsed = chrono::Utc::now() - start;
416
417 if res == 0 {
418 let rate = elapsed
419 .checked_div(sz_done as i32)
420 .and_then(|r| r.num_microseconds())
421 .unwrap_or(0);
422 let bps = if rate > 0 {
423 1_000_000.0 / rate as f32
424 } else {
425 0.
426 };
427
428 let u = UpdateTransferProgress {
429 cur_xfer: None,
430 percent: 100,
431 bps,
432 remaining: chrono::Duration::zero(),
433 duration: elapsed,
434 complete: false,
435 };
436
437 progress(&u);
438 } else {
439 error!("firmware transfer error: 0x{:02x}", res);
440 }
441 let mut fw_resp = fw_req.response();
442 fw_resp.cc = 0;
443 pldm::pldm_tx_resp(&mut req_ep, &fw_resp)?;
444 break;
445 }
446 _ => {
447 return Err(PldmUpdateError::new_proto(format!(
448 "unexpected command during update: {fw_req:?}"
449 )));
450 }
451 }
452 }
453
454 let (fw_req, mut req_ep) = pldm::pldm_rx_req(listener)?;
456 match fw_req.cmd {
457 0x17 => {
458 let res = fw_req.data[0];
459 if res != 0 {
460 return Err(PldmUpdateError::new_update(
461 "firmware verify failure".into(),
462 ));
463 }
464 }
465 _ => {
466 return Err(PldmUpdateError::new_update(
467 "unexpected command in verify state".into(),
468 ))
469 }
470 }
471 let mut fw_resp = fw_req.response();
472 fw_resp.cc = 0;
473 pldm::pldm_tx_resp(&mut req_ep, &fw_resp)?;
474 drop(req_ep);
475
476 let (fw_req, mut req_ep) = pldm::pldm_rx_req(listener)?;
478 match fw_req.cmd {
479 0x18 => {
480 let res = fw_req.data[0];
481 if res != 0 {
482 return Err(PldmUpdateError::new_update(
483 "firmware apply failure".into(),
484 ));
485 }
486 }
487 _ => {
488 return Err(PldmUpdateError::new_update(
489 "unexpected command in apply state".into(),
490 ));
491 }
492 }
493
494 let mut fw_resp = fw_req.response();
495 fw_resp.cc = 0;
496 pldm::pldm_tx_resp(&mut req_ep, &fw_resp)?;
497
498 check_fd_state(comm, PldmFDState::ReadyXfer)?;
499
500 Ok(())
501}
502
503pub fn update_components(
504 comm: &mut impl mctp::ReqChannel,
505 listener: &mut impl mctp::Listener,
506 update: &mut Update,
507) -> Result<()> {
508 update_components_progress(comm, listener, update, |_| ())
509}
510
511pub fn update_components_progress<F>(
512 comm: &mut impl mctp::ReqChannel,
513 listener: &mut impl mctp::Listener,
514 update: &mut Update,
515 mut progress: F,
516) -> Result<()>
517where
518 F: FnMut(&UpdateTransferProgress),
519{
520 let components = update.components.clone();
521
522 for idx in components {
523 let component = update.package.components.get(idx).unwrap();
524 update_component_progress(
525 comm,
526 listener,
527 &update.package,
528 component,
529 update.index,
530 &mut progress,
531 )?;
532 }
533
534 Ok(())
535}
536
537pub fn activate_firmware(
538 comm: &mut impl mctp::ReqChannel,
539 self_activate: bool,
540) -> Result<()> {
541 check_fd_state(comm, PldmFDState::ReadyXfer)?;
542
543 let self_activation_req: u8 = if self_activate { 1 } else { 0 };
544
545 let mut data = vec![];
546 data.extend_from_slice(&self_activation_req.to_le_bytes());
547
548 let req = pldm::PldmRequest::new_data(PLDM_TYPE_FW, 0x1a, data);
549 let rsp = pldm::pldm_xfer(comm, req)?;
550
551 if rsp.cc == 0 || rsp.cc == FwCode::ACTIVATION_NOT_REQUIRED as u8 {
552 Ok(())
553 } else {
554 Err(PldmUpdateError::new_command(0x1a, rsp.cc))
555 }
556}
557
558fn check_fd_state(
559 comm: &mut impl mctp::ReqChannel,
560 expected_state: PldmFDState,
561) -> Result<()> {
562 let req = pldm::PldmRequest::new(PLDM_TYPE_FW, 0x1b);
563 let rsp = pldm::pldm_xfer(comm, req)?;
564
565 if rsp.cc != 0 {
566 return Err(PldmUpdateError::new_command(0x1b, rsp.cc));
567 }
568
569 let (_, res) = all_consuming(GetStatusResponse::parse)(rsp.data.as_ref())
570 .map_err(|_e| {
571 PldmUpdateError::new_proto("can't parse Get Status response".into())
572 })?;
573
574 debug!("state: {:?}", res.current_state);
576
577 if res.current_state != expected_state {
578 return Err(PldmUpdateError::new_proto(format!(
579 "invalid state {:?}",
580 res.current_state
581 )));
582 }
583
584 Ok(())
585}