mirror of
https://github.com/freemkv/freemkv-unlock.git
synced 2026-10-07 18:13:11 +00:00
renesas: vendor CDBs from pioneer-optical
The identity read (3C 02 F1), the two gated memory reads (3C 02 B0 at 0x04 and 0x500000, 0xA4 bytes) and the read-unlock knock (3B 02 41 A5 AA AA) come from the crate's constructors; the bytes on the wire are unchanged and the knock test still pins them. The crate's flash::read_memory is not used: it knocks before every read and stops on a refused knock, where this sequence reads first and tries the second window even when the knock is refused.
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- The Renesas/Pioneer unlocker builds its vendor CDBs (identity read, gated memory reads, read-unlock knock) with the `pioneer-optical` crate instead of hard-coded bytes; the commands sent are unchanged.
|
||||||
- AACS handshake diagnostics that a scan now reports itself log below WARN: unsupported or unverifiable drive certs, VID read failures and transport faults at DEBUG, `read_data_key_dropped` at INFO.
|
- AACS handshake diagnostics that a scan now reports itself log below WARN: unsupported or unverifiable drive certs, VID read failures and transport faults at DEBUG, `read_data_key_dropped` at INFO.
|
||||||
|
|
||||||
## [1.7.7] — 2026-09-26
|
## [1.7.7] — 2026-09-26
|
||||||
|
|||||||
Generated
+7
@@ -110,6 +110,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"pioneer-optical",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -207,6 +208,12 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pioneer-optical"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48a5bdb9e9a07422ceb5133d227fbe4ecce77060007e5a9bac0dda3d82d72911"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.107"
|
version = "1.0.107"
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ rand = "0.10"
|
|||||||
# material on drop so it doesn't linger in freed memory (defence in depth atop
|
# material on drop so it doesn't linger in freed memory (defence in depth atop
|
||||||
# the redacting Debug impls). `derive` gives ZeroizeOnDrop for the key structs.
|
# the redacting Debug impls). `derive` gives ZeroizeOnDrop for the key structs.
|
||||||
zeroize = { version = "1", features = ["derive"] }
|
zeroize = { version = "1", features = ["derive"] }
|
||||||
|
# Pioneer/Renesas vendor CDB constructors (identity read, gated memory read, knock).
|
||||||
|
pioneer-optical = "0.7"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
uninlined_format_args = "allow"
|
uninlined_format_args = "allow"
|
||||||
|
|||||||
+19
-14
@@ -9,9 +9,8 @@
|
|||||||
use crate::scsi::{DataDirection, ScsiTransport, is_dead_bus};
|
use crate::scsi::{DataDirection, ScsiTransport, is_dead_bus};
|
||||||
use crate::{UnlockCtx, UnlockError, Unlocked, Unlocker};
|
use crate::{UnlockCtx, UnlockError, Unlocked, Unlocker};
|
||||||
|
|
||||||
/// READ_BUFFER mode 0x02, buffer 0xF1 — the Renesas vendor identity buffer.
|
/// The Renesas vendor identity block length (READ_BUFFER 0x02/0xF1).
|
||||||
const RB_F1_CDB: [u8; 10] = [0x3C, 0x02, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00];
|
const RB_F1_LEN: usize = pioneer_optical::IDENTITY_LEN as usize;
|
||||||
const RB_F1_LEN: usize = 48;
|
|
||||||
/// The ASCII interface marker a Renesas controller returns at `[16..19]`.
|
/// The ASCII interface marker a Renesas controller returns at `[16..19]`.
|
||||||
const RENESAS_MARKER: &[u8] = b"SAT";
|
const RENESAS_MARKER: &[u8] = b"SAT";
|
||||||
const RENESAS_MARKER_OFFSET: usize = 16;
|
const RENESAS_MARKER_OFFSET: usize = 16;
|
||||||
@@ -27,7 +26,8 @@ const RENESAS_MARKER_OFFSET: usize = 16;
|
|||||||
/// `Err(Transport)` on a dead bus.
|
/// `Err(Transport)` on a dead bus.
|
||||||
pub fn is_renesas(scsi: &mut dyn ScsiTransport) -> std::result::Result<bool, UnlockError> {
|
pub fn is_renesas(scsi: &mut dyn ScsiTransport) -> std::result::Result<bool, UnlockError> {
|
||||||
let mut buf = [0u8; RB_F1_LEN];
|
let mut buf = [0u8; RB_F1_LEN];
|
||||||
match scsi.execute(&RB_F1_CDB, DataDirection::FromDevice, &mut buf, 5_000) {
|
let cdb = pioneer_optical::vendor_identity();
|
||||||
|
match scsi.execute(&cdb, DataDirection::FromDevice, &mut buf, 5_000) {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
let end = RENESAS_MARKER_OFFSET + RENESAS_MARKER.len();
|
let end = RENESAS_MARKER_OFFSET + RENESAS_MARKER.len();
|
||||||
Ok(r.status == 0
|
Ok(r.status == 0
|
||||||
@@ -72,27 +72,32 @@ impl Renesas {
|
|||||||
// MakeMKV's vendor "open" sequence: primary read (A), and on refusal a
|
// MakeMKV's vendor "open" sequence: primary read (A), and on refusal a
|
||||||
// knock + second read (B).
|
// knock + second read (B).
|
||||||
fn vendor_open(scsi: &mut dyn ScsiTransport) -> std::result::Result<bool, UnlockError> {
|
fn vendor_open(scsi: &mut dyn ScsiTransport) -> std::result::Result<bool, UnlockError> {
|
||||||
const RB_B0_04_CDB: [u8; 10] = [0x3C, 0x02, 0xB0, 0x00, 0x00, 0x04, 0x00, 0x00, 0xA4, 0x00];
|
|
||||||
const KNOCK_A5AAAA_CDB: [u8; 10] =
|
|
||||||
[0x3B, 0x02, 0x41, 0xA5, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00];
|
|
||||||
const RB_B0_500000_CDB: [u8; 10] =
|
|
||||||
[0x3C, 0x02, 0xB0, 0x50, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x00];
|
|
||||||
|
|
||||||
// A: primary open read.
|
// A: primary open read.
|
||||||
if read_is_good(scsi, &RB_B0_04_CDB)? {
|
if read_is_good(scsi, &pioneer_optical::read_memory(0x04, OPEN_READ_LEN))? {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
// B: MakeMKV's fallback — knock, then the second-window read. The knock is
|
// B: MakeMKV's fallback — knock, then the second-window read. The knock is
|
||||||
// fire-and-forget (payload-less); its own status is not the signal.
|
// fire-and-forget (payload-less); its own status is not the signal.
|
||||||
match scsi.execute(&KNOCK_A5AAAA_CDB, DataDirection::None, &mut [], 5_000) {
|
match scsi.execute(
|
||||||
|
&pioneer_optical::knock(),
|
||||||
|
DataDirection::None,
|
||||||
|
&mut [],
|
||||||
|
5_000,
|
||||||
|
) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) if is_dead_bus(&e) => return Err(UnlockError::Transport),
|
Err(e) if is_dead_bus(&e) => return Err(UnlockError::Transport),
|
||||||
Err(_) => {} // a drive that refuses the knock still gets the B read tried
|
Err(_) => {} // a drive that refuses the knock still gets the B read tried
|
||||||
}
|
}
|
||||||
read_is_good(scsi, &RB_B0_500000_CDB)
|
read_is_good(
|
||||||
|
scsi,
|
||||||
|
&pioneer_optical::read_memory(0x50_0000, OPEN_READ_LEN),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The vendor "open" reads' length (0xA4 = 164 bytes).
|
||||||
|
const OPEN_READ_LEN: u32 = 0xA4;
|
||||||
|
|
||||||
/// Issue a 164-byte vendor READ_BUFFER; `Ok(true)` on GOOD status, `Ok(false)`
|
/// Issue a 164-byte vendor READ_BUFFER; `Ok(true)` on GOOD status, `Ok(false)`
|
||||||
/// on CHECK CONDITION (drive refused), `Err(Transport)` on a senseless
|
/// on CHECK CONDITION (drive refused), `Err(Transport)` on a senseless
|
||||||
/// transport failure (dead bus).
|
/// transport failure (dead bus).
|
||||||
@@ -100,7 +105,7 @@ fn read_is_good(
|
|||||||
scsi: &mut dyn ScsiTransport,
|
scsi: &mut dyn ScsiTransport,
|
||||||
cdb: &[u8; 10],
|
cdb: &[u8; 10],
|
||||||
) -> std::result::Result<bool, UnlockError> {
|
) -> std::result::Result<bool, UnlockError> {
|
||||||
let mut buf = [0u8; 164];
|
let mut buf = [0u8; OPEN_READ_LEN as usize];
|
||||||
match scsi.execute(cdb, DataDirection::FromDevice, &mut buf, 5_000) {
|
match scsi.execute(cdb, DataDirection::FromDevice, &mut buf, 5_000) {
|
||||||
Ok(r) => Ok(r.status == 0),
|
Ok(r) => Ok(r.status == 0),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user