Initial commit: LibreDrive unlocker plugin for libfreemkv
New crate freemkv-unlock-ld — the LibreDrive Unlocker implementation, split out of libfreemkv so the library stays firmware-clean on crates.io. Owns everything about HOW MediaTek MT1959 drives are firmware-unlocked: - profiles.json (the bundled drive-profile database + DriveProfile parse) - src/platform/mt1959/* (variant-A/B firmware upload + unlock handshake) - WRITE_BUFFER / MODE SELECT upload, unlock CDBs, disc-speed calibration Exposes LibreDrive::new() implementing libfreemkv::Unlocker (name/matches/ unlock). Plug it in with one line at process start: libfreemkv::register_unlocker(Box::new(freemkv_unlock_ld::LibreDrive::new())); Depends on libfreemkv (path-patched via gitignored .cargo/config.toml in dev) for the trait, ScsiTransport, DriveId, and Result. README notes it's the LibreDrive unlocker (attribution to be added by owner).
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
//! MT1959 variant A firmware upload.
|
||||
//!
|
||||
//! WRITE_BUFFER (0x3B) → verify READ_BUFFER (0x45) → unlock × 2
|
||||
|
||||
use super::Mt1959;
|
||||
use crate::error::Result;
|
||||
use crate::scsi::{DataDirection, ScsiTransport};
|
||||
|
||||
use super::SCSI_WRITE_BUFFER;
|
||||
|
||||
const VERIFY_BUFFER_ID: u8 = 0x45;
|
||||
|
||||
/// WRITE_BUFFER carries a 24-bit transfer length, so a firmware blob
|
||||
/// larger than this cannot be uploaded in one command.
|
||||
const WRITE_BUFFER_MAX_LEN: usize = 0x00FF_FFFF;
|
||||
|
||||
pub(super) fn load_firmware(mt: &mut Mt1959, scsi: &mut dyn ScsiTransport) -> Result<()> {
|
||||
let firmware = &mt.profile.firmware;
|
||||
if firmware.is_empty() {
|
||||
return Err(crate::error::Error::UnlockFailed);
|
||||
}
|
||||
|
||||
// Upload firmware via WRITE_BUFFER. The CDB's length is a 24-bit field;
|
||||
// if the blob exceeds that, the encoded length would silently disagree
|
||||
// with the bytes actually sent (`data`). Reject rather than upload a
|
||||
// length-mismatched command.
|
||||
let len = firmware.len();
|
||||
if len > WRITE_BUFFER_MAX_LEN {
|
||||
return Err(crate::error::Error::UnlockFailed);
|
||||
}
|
||||
let cdb = [
|
||||
SCSI_WRITE_BUFFER,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
(len >> 16) as u8,
|
||||
(len >> 8) as u8,
|
||||
len as u8,
|
||||
0x00,
|
||||
];
|
||||
let mut data = firmware.clone();
|
||||
scsi.execute(&cdb, DataDirection::ToDevice, &mut data, 30_000)?;
|
||||
|
||||
// Verify firmware loaded (non-fatal — different buffer_id 0x45)
|
||||
let verify_cdb = [
|
||||
super::SCSI_READ_BUFFER,
|
||||
super::MODE_A,
|
||||
VERIFY_BUFFER_ID,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
super::VALIDATE_RESPONSE_SIZE,
|
||||
0x00,
|
||||
];
|
||||
let mut verify_resp = [0u8; super::VALIDATE_RESPONSE_SIZE as usize];
|
||||
let _ = scsi.execute(
|
||||
&verify_cdb,
|
||||
DataDirection::FromDevice,
|
||||
&mut verify_resp,
|
||||
5_000,
|
||||
);
|
||||
|
||||
// Double unlock after firmware upload. The first establishes the
|
||||
// unlock and is fatal on failure; the second is a confirmation pass and
|
||||
// is best-effort (matching variant B), so a benign hiccup on the
|
||||
// redundant call doesn't fail an already-successful unlock.
|
||||
mt.do_unlock(scsi)?;
|
||||
let _ = mt.do_unlock(scsi);
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user