mt1959_b: per-drive fw_verify_cdb + correct firmware length; v1.0.0-rc.4.3

The variant-B firmware load hardcoded one drive's F1 vendor-verify token
and a fixed 0x9C0 firmware size. Across the 140 B drives the verify CDB
has 39 distinct per-drive values, and the real firmware length is encoded
in each drive's MODE SELECT CDB (2192..2528 bytes) — so the constants were
wrong for ~139 of 140 drives and truncated 13.

- DriveProfile gains fw_verify_cdb (the per-drive 0xF1 verify).
- variant_b uploads firmware.len() (now the correct per-drive size from
  the regenerated profiles.json) and issues profile.fw_verify_cdb, falling
  back to the const only for legacy profiles.
- profiles.json regenerated: 79 firmware fields corrected (13 truncated
  recovered to full length, 66 over-reads trimmed).
- Adds a recording-transport regression test asserting the profile's F1
  verify is issued (not the const) and MODE SELECT carries firmware.len().
This commit is contained in:
Matthew Jackson
2026-06-23 15:40:38 -07:00
parent feacfbb10b
commit 15e263451e
5 changed files with 161 additions and 90 deletions
+16 -9
View File
@@ -7,8 +7,10 @@ use crate::error::Result;
use crate::scsi::{DataDirection, ScsiTransport};
const SCSI_MODE_SELECT: u8 = 0x55;
const FIRMWARE_MAX_SIZE: usize = 0x9C0;
const FIRMWARE_EXTRA: [u8; 16] = [0; 16];
/// Fallback F1 vendor-verify for legacy profiles that predate the per-drive
/// `fw_verify_cdb` capture. It carries ONE drive's token, so it only works for
/// that drive — real profiles must supply their own (see `DriveProfile`).
const VENDOR_VERIFY: [u8; 10] = [0xF1, 0x01, 0x02, 0x00, 0x0D, 0x30, 0x01, 0xF3, 0xAD, 0x23];
pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Result<()> {
@@ -17,14 +19,16 @@ pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Re
return Err(crate::error::Error::UnlockFailed);
}
// Step 1: Upload firmware via MODE SELECT. Variant-B firmware blobs are
// exactly FIRMWARE_MAX_SIZE; a larger blob means a corrupt/wrong profile,
// and silently truncating it would upload a partial image that can't
// unlock. Reject it explicitly instead.
if firmware.len() > FIRMWARE_MAX_SIZE {
// Step 1: Upload the firmware via MODE SELECT. The profile's `firmware` is
// the exact per-drive image — extracted at the drive's own load-CDB length
// (2192..2528 bytes; the old fixed 0x9C0 truncated some drives and over-read
// others into blob strings). Upload all of it. MODE SELECT(10)'s
// parameter-list length is 16-bit, so reject only a blob that can't be
// expressed in the CDB.
let write_len = firmware.len();
if write_len > u16::MAX as usize {
return Err(crate::error::Error::UnlockFailed);
}
let write_len = FIRMWARE_MAX_SIZE.min(firmware.len());
let mode_select_cdb = [
SCSI_MODE_SELECT,
0x10,
@@ -77,9 +81,12 @@ pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Re
let mut data2 = FIRMWARE_EXTRA.to_vec();
let _ = scsi.execute(&write_extra_cdb, DataDirection::ToDevice, &mut data2, 5_000);
// Step 4: Vendor verify (0xF1 — B-only, not standard SCSI)
// Step 4: Vendor verify (0xF1 — B-only, not standard SCSI). PER-DRIVE: take
// it from the profile (39 distinct values across the 140 B drives). The
// const is only a legacy fallback — it carries one drive's token.
let verify_cdb = mt.profile.fw_verify_cdb.unwrap_or(VENDOR_VERIFY);
let mut dummy = [0u8; 0];
let _ = scsi.execute(&VENDOR_VERIFY, DataDirection::None, &mut dummy, 5_000);
let _ = scsi.execute(&verify_cdb, DataDirection::None, &mut dummy, 5_000);
// Step 5: Unlock retries (up to 5, then a final fatal attempt). On a
// successful unlock we issue one confirmation pass; its result is