From e6356e852caa9bcc69707b3f09228c363b38b8b6 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Mon, 7 Sep 2026 18:35:44 -0400 Subject: [PATCH 1/9] test --- debug.ixx | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/debug.ixx b/debug.ixx index 89ef7f01..0ed476bb 100644 --- a/debug.ixx +++ b/debug.ixx @@ -124,6 +124,38 @@ export int redumper_debug(Context &ctx, Options &options) std::filesystem::path physical_path(image_prefix + ".physical"); std::filesystem::path sub_path(image_prefix + ".subcode"); + if(1) + { + constexpr uint32_t buffer_offset = 0x1afffe; + constexpr uint32_t boundary_length = 0x102; + constexpr uint32_t full_length = 0xe42102; + constexpr uint32_t firmware_entry_offset = 0xdd0052; + constexpr uint32_t firmware_entry_length = 64; + constexpr uint32_t ff2000_offset = 0xe42002; + constexpr uint32_t ff2000_length = 256; + + SPTD sptd(options.drive, options.scsi_timeout); + + std::vector boundary_buffer(boundary_length); + if(auto status = cmd_read_buffer(sptd, boundary_buffer.data(), boundary_buffer.size(), READ_BUFFER_Mode::READ_DATA, buffer_offset, boundary_length); status.status_code) + throw_line("READ BUFFER boundary probe failed, SCSI ({})", SPTD::StatusMessage(status)); + write_vector("read_buffer_boundary.debug", boundary_buffer); + + std::vector full_buffer(full_length); + if(auto status = cmd_read_buffer(sptd, full_buffer.data(), full_buffer.size(), READ_BUFFER_Mode::READ_DATA, buffer_offset, full_length); status.status_code) + throw_line("READ BUFFER full probe failed, SCSI ({})", SPTD::StatusMessage(status)); + write_vector("read_buffer_full.debug", full_buffer); + + std::vector firmware_entry(full_buffer.begin() + firmware_entry_offset, full_buffer.begin() + firmware_entry_offset + firmware_entry_length); + std::vector ff2000(full_buffer.begin() + ff2000_offset, full_buffer.begin() + ff2000_offset + ff2000_length); + write_vector("read_buffer_f80050.debug", firmware_entry); + write_vector("read_buffer_ff2000.debug", ff2000); + + const uint8_t expected_firmware_entry[] = { 0x8a, 0xfd, 0xce, 0xa9 }; + LOG("firmware entry signature: {}", memcmp(firmware_entry.data(), expected_firmware_entry, sizeof(expected_firmware_entry)) ? "mismatch" : "match"); + LOG(""); + } + if(0) { // auto ss = read_vector("security_sector3.debug"); From ca873498be0d320b78dfcfa940035fe2cbeefa67 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Mon, 7 Sep 2026 18:45:01 -0400 Subject: [PATCH 2/9] another test --- debug.ixx | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/debug.ixx b/debug.ixx index 0ed476bb..85629d62 100644 --- a/debug.ixx +++ b/debug.ixx @@ -126,33 +126,34 @@ export int redumper_debug(Context &ctx, Options &options) if(1) { - constexpr uint32_t buffer_offset = 0x1afffe; - constexpr uint32_t boundary_length = 0x102; - constexpr uint32_t full_length = 0xe42102; - constexpr uint32_t firmware_entry_offset = 0xdd0052; - constexpr uint32_t firmware_entry_length = 64; - constexpr uint32_t ff2000_offset = 0xe42002; - constexpr uint32_t ff2000_length = 256; - SPTD sptd(options.drive, options.scsi_timeout); - std::vector boundary_buffer(boundary_length); - if(auto status = cmd_read_buffer(sptd, boundary_buffer.data(), boundary_buffer.size(), READ_BUFFER_Mode::READ_DATA, buffer_offset, boundary_length); status.status_code) - throw_line("READ BUFFER boundary probe failed, SCSI ({})", SPTD::StatusMessage(status)); - write_vector("read_buffer_boundary.debug", boundary_buffer); + auto read_memory = [&sptd](uint32_t address, uint16_t length) + { + uint8_t cdb[12] = {}; + cdb[0] = 0xf5; + cdb[4] = (uint8_t)(address >> 24); + cdb[5] = (uint8_t)(address >> 16); + cdb[6] = (uint8_t)(address >> 8); + cdb[7] = (uint8_t)address; + cdb[8] = (uint8_t)(length >> 8); + cdb[9] = (uint8_t)length; + + std::vector data(length); + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), data.data(), data.size()); status.status_code) + throw_line("F5 memory read failed at 0x{:08x}, SCSI ({})", address, SPTD::StatusMessage(status)); - std::vector full_buffer(full_length); - if(auto status = cmd_read_buffer(sptd, full_buffer.data(), full_buffer.size(), READ_BUFFER_Mode::READ_DATA, buffer_offset, full_length); status.status_code) - throw_line("READ BUFFER full probe failed, SCSI ({})", SPTD::StatusMessage(status)); - write_vector("read_buffer_full.debug", full_buffer); + return data; + }; - std::vector firmware_entry(full_buffer.begin() + firmware_entry_offset, full_buffer.begin() + firmware_entry_offset + firmware_entry_length); - std::vector ff2000(full_buffer.begin() + ff2000_offset, full_buffer.begin() + ff2000_offset + ff2000_length); - write_vector("read_buffer_f80050.debug", firmware_entry); - write_vector("read_buffer_ff2000.debug", ff2000); + auto firmware_entry = read_memory(0x00f80050, 64); + write_vector("read_memory_f80050.debug", firmware_entry); const uint8_t expected_firmware_entry[] = { 0x8a, 0xfd, 0xce, 0xa9 }; LOG("firmware entry signature: {}", memcmp(firmware_entry.data(), expected_firmware_entry, sizeof(expected_firmware_entry)) ? "mismatch" : "match"); + + auto ff2000 = read_memory(0x00ff2000, 256); + write_vector("read_memory_ff2000.debug", ff2000); LOG(""); } From bc01894c53dce5f58999fb42ae6bbd96b5f84af0 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Mon, 7 Sep 2026 18:54:03 -0400 Subject: [PATCH 3/9] better test --- debug.ixx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/debug.ixx b/debug.ixx index 85629d62..feca7489 100644 --- a/debug.ixx +++ b/debug.ixx @@ -146,14 +146,15 @@ export int redumper_debug(Context &ctx, Options &options) return data; }; - auto firmware_entry = read_memory(0x00f80050, 64); - write_vector("read_memory_f80050.debug", firmware_entry); - - const uint8_t expected_firmware_entry[] = { 0x8a, 0xfd, 0xce, 0xa9 }; - LOG("firmware entry signature: {}", memcmp(firmware_entry.data(), expected_firmware_entry, sizeof(expected_firmware_entry)) ? "mismatch" : "match"); - - auto ff2000 = read_memory(0x00ff2000, 256); - write_vector("read_memory_ff2000.debug", ff2000); + std::vector high_memory; + high_memory.reserve(0x10000); + for(uint32_t address = 0x00ff0000; address < 0x01000000; address += 0x4000) + { + LOG("reading memory at 0x{:08x}", address); + auto chunk = read_memory(address, 0x4000); + high_memory.insert(high_memory.end(), chunk.begin(), chunk.end()); + } + write_vector("read_memory_ff0000.debug", high_memory); LOG(""); } From 4fd12aa381d1c80938f944029e311dbeed3c240a Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Mon, 7 Sep 2026 20:32:02 -0400 Subject: [PATCH 4/9] experiment --- debug.ixx | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/debug.ixx b/debug.ixx index feca7489..0a5e70a2 100644 --- a/debug.ixx +++ b/debug.ixx @@ -124,6 +124,63 @@ export int redumper_debug(Context &ctx, Options &options) std::filesystem::path physical_path(image_prefix + ".physical"); std::filesystem::path sub_path(image_prefix + ".subcode"); + if(1) + { + SPTD sptd(options.drive, options.scsi_timeout); + + auto drive_query = cmd_drive_query(sptd); + bool monitor_mode = drive_query.vendor_id == "PLEXTOR" && drive_query.product_id == "MONT"; + bool firmware_mode = drive_query.vendor_id == "PLEXTOR" && drive_query.product_id.find("PX-W5224A") != std::string::npos; + + LOG("Plextor boot-state probe"); + LOG(" inquiry: {} {} {}", drive_query.vendor_id, drive_query.product_id, drive_query.product_revision_level); + LOG(" active mode: {}", monitor_mode ? "recovery monitor" : firmware_mode ? "normal firmware" : "unknown"); + + uint8_t port_mask = 0; + for(uint8_t mode = 0; mode <= 4; ++mode) + { + uint8_t cdb[12] = {}; + cdb[0] = 0xde; + cdb[1] = mode; + + uint8_t response[2] = {}; + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, sizeof(response)); status.status_code) + throw_line("DE mode {} probe failed, SCSI ({})", mode, SPTD::StatusMessage(status)); + + LOG(" DE mode {}: {:02x} {:02x} ({})", mode, response[0], response[1], + (uint8_t)(response[0] ^ response[1]) == 0xff ? "complement valid" : "complement invalid"); + if(mode == 0) + port_mask = response[0]; + } + + LOG(" P0.1: {}", port_mask & 0x01 ? "low" : "high"); + LOG(" P0.2: {}", port_mask & 0x02 ? "low" : "high"); + LOG(" P0.6: {}", port_mask & 0x04 ? "low" : "high"); + LOG(" P0.0: {}", port_mask & 0x08 ? "low" : "high"); + LOG(" P0.5: {}", port_mask & 0x10 ? "low" : "high"); + + uint8_t cdb[12] = {}; + cdb[0] = 0xf5; + cdb[5] = 0xff; + cdb[6] = 0xc0; + cdb[7] = 0x1e; + cdb[9] = 2; + + uint8_t configuration[2] = {}; + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), configuration, sizeof(configuration)); status.status_code) + throw_line("F5 configuration read failed, SCSI ({})", SPTD::StatusMessage(status)); + + uint8_t configuration_value = configuration[1]; + uint32_t low_count = std::popcount((uint32_t)(port_mask & 0x07)); + bool visible_recovery_gates = low_count < 2 && ((port_mask & 0x04) == 0 || configuration_value != 0) + && configuration_value != 0x11 && configuration_value != 0x12; + + LOG(" configuration entry 0x0f at 0x00ffc01e: {:02x} {:02x}", configuration[0], configuration[1]); + LOG(" forced-recovery gates excluding P1.6: {}", visible_recovery_gates ? "pass" : "fail"); + LOG(" P1.6: not observable through DE/F5"); + LOG(""); + } + if(1) { SPTD sptd(options.drive, options.scsi_timeout); From 7e4f9f57193498280bd7132c012c518b81c05e54 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Mon, 7 Sep 2026 21:29:25 -0400 Subject: [PATCH 5/9] support any windows device names --- scsi/sptd.ixx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scsi/sptd.ixx b/scsi/sptd.ixx index 5db418f0..82f9060c 100644 --- a/scsi/sptd.ixx +++ b/scsi/sptd.ixx @@ -65,7 +65,11 @@ public: #endif { #if defined(_WIN32) - _handle = CreateFile(std::format("//./{}:", drive_path[0]).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + std::string device_name = drive_path; + if(device_name.size() == 1) + device_name += ':'; + + _handle = CreateFile(std::format("//./{}", device_name).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if(_handle == INVALID_HANDLE_VALUE) throw_line("unable to open drive ({}, SYSTEM: {})", drive_path, getLastError()); #elif defined(__APPLE__) From 55fc3e29b615d5b57a25ab6b1134d821ac3a079b Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Sat, 12 Sep 2026 14:10:50 -0400 Subject: [PATCH 6/9] SCSI transfer fix --- cd/cd_common.ixx | 5 ++++- debug.ixx | 10 +++++----- drive/plextor.ixx | 2 +- drive/test.ixx | 2 +- scsi/cmd.ixx | 51 ++++++++++++++++++++++++----------------------- scsi/sptd.ixx | 16 ++++++++++++--- 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/cd/cd_common.ixx b/cd/cd_common.ixx index 9d246f87..0cb10001 100644 --- a/cd/cd_common.ixx +++ b/cd/cd_common.ixx @@ -468,7 +468,10 @@ export SPTD::Status read_sector(SPTD &sptd, uint8_t *sector, bool &unscrambled, if(drive_config.read_method == ReadMethod::D8) { auto sub_code = drive_config.sector_order == SectorOrder::DATA_SUB ? READ_CDDA_SubCode::DATA_SUB : READ_CDDA_SubCode::DATA_C2_SUB; - status = cmd_read_cdda(sptd, sector_buffer.data(), CD_RAW_DATA_SIZE, lba, sectors_count, sub_code); + auto [result, transferred_length] = cmd_read_cdda(sptd, sector_buffer.data(), layout.size, lba, sectors_count, sub_code); + status = result; + if(!status.status_code && transferred_length != layout.size * sectors_count) + status.status_code = SPTD::HOST_SHORT_TRANSFER; } else { diff --git a/debug.ixx b/debug.ixx index 0a5e70a2..1ac75f89 100644 --- a/debug.ixx +++ b/debug.ixx @@ -144,7 +144,7 @@ export int redumper_debug(Context &ctx, Options &options) cdb[1] = mode; uint8_t response[2] = {}; - if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, sizeof(response)); status.status_code) + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, sizeof(response)).first; status.status_code) throw_line("DE mode {} probe failed, SCSI ({})", mode, SPTD::StatusMessage(status)); LOG(" DE mode {}: {:02x} {:02x} ({})", mode, response[0], response[1], @@ -167,7 +167,7 @@ export int redumper_debug(Context &ctx, Options &options) cdb[9] = 2; uint8_t configuration[2] = {}; - if(auto status = sptd.sendCommand(cdb, sizeof(cdb), configuration, sizeof(configuration)); status.status_code) + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), configuration, sizeof(configuration)).first; status.status_code) throw_line("F5 configuration read failed, SCSI ({})", SPTD::StatusMessage(status)); uint8_t configuration_value = configuration[1]; @@ -181,7 +181,7 @@ export int redumper_debug(Context &ctx, Options &options) LOG(""); } - if(1) + if(0) { SPTD sptd(options.drive, options.scsi_timeout); @@ -197,7 +197,7 @@ export int redumper_debug(Context &ctx, Options &options) cdb[9] = (uint8_t)length; std::vector data(length); - if(auto status = sptd.sendCommand(cdb, sizeof(cdb), data.data(), data.size()); status.status_code) + if(auto status = sptd.sendCommand(cdb, sizeof(cdb), data.data(), data.size()).first; status.status_code) throw_line("F5 memory read failed at 0x{:08x}, SCSI ({})", address, SPTD::StatusMessage(status)); return data; @@ -396,7 +396,7 @@ export int redumper_debug(Context &ctx, Options &options) } // MEDIATEK cache dump extract - if(1) + if(0) { std::vector cache = read_vector(cache_path); diff --git a/drive/plextor.ixx b/drive/plextor.ixx index a1420785..120108f9 100644 --- a/drive/plextor.ixx +++ b/drive/plextor.ixx @@ -57,7 +57,7 @@ export PlextorLeadIn plextor_leadin_read(SPTD &sptd, uint32_t tail_size) LOGC_RF("{} [LBA: {}]", spinner_animation(), neg); std::vector sector_buffer(CD_RAW_DATA_SIZE); - SPTD::Status status = cmd_read_cdda(sptd, sector_buffer.data(), sector_buffer.size(), neg, 1, READ_CDDA_SubCode::DATA_SUB); + SPTD::Status status = cmd_read_cdda(sptd, sector_buffer.data(), sector_buffer.size(), neg, 1, READ_CDDA_SubCode::DATA_SUB).first; if(!status.status_code) { diff --git a/drive/test.ixx b/drive/test.ixx index 2d4651f2..730ad758 100644 --- a/drive/test.ixx +++ b/drive/test.ixx @@ -269,7 +269,7 @@ export int redumper_drive_test(Context &ctx, Options &options) for(auto sc : SUB_CODE_STRING) { std::vector sector_buffer(CD_DATA_SIZE + CD_C2_SIZE + CD_SUBCODE_SIZE); - status = cmd_read_cdda(*ctx.sptd, sector_buffer.data(), sector_buffer.size(), std::get<1>(t), 1, sc.first); + status = cmd_read_cdda(*ctx.sptd, sector_buffer.data(), sector_buffer.size(), std::get<1>(t), 1, sc.first).first; if(status.status_code) { if(options.verbose) diff --git a/scsi/cmd.ixx b/scsi/cmd.ixx index 1775fba3..322c0470 100644 --- a/scsi/cmd.ixx +++ b/scsi/cmd.ixx @@ -2,6 +2,7 @@ module; #include #include #include +#include #include export module scsi.cmd; @@ -49,7 +50,7 @@ SPTD::Status cdb_send_receive(SPTD &sptd, std::vector &response, T &cdb response.resize(initial_size); *(uint16_t *)cdb.allocation_length = endian_swap(response.size()); - status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()); + status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()).first; if(status.status_code) { response.clear(); @@ -63,7 +64,7 @@ SPTD::Status cdb_send_receive(SPTD &sptd, std::vector &response, T &cdb *(uint16_t *)cdb.allocation_length = endian_swap(response_size); - status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()); + status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), (uint32_t)response.size()).first; if(status.status_code) response_size = 0; // always use the size from the latest read attempt, this fixes some identified KREON issues @@ -83,7 +84,7 @@ export SPTD::Status cmd_drive_ready(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::TEST_UNIT_READY; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -97,7 +98,7 @@ export SPTD::Status cmd_inquiry(SPTD &sptd, uint8_t *data, uint32_t data_size, I *(uint16_t *)cdb.allocation_length = endian_swap(data_size); - return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size); + return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size).first; } @@ -111,7 +112,7 @@ export SPTD::Status cmd_read_capacity(SPTD &sptd, uint32_t &lba, uint32_t &block READ_CAPACITY_Response response; - auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, sizeof(response)); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, sizeof(response)).first; if(!status.status_code) { lba = endian_swap(response.address); @@ -176,7 +177,7 @@ export SPTD::Status cmd_send_key(SPTD &sptd, const uint8_t *data, uint32_t data_ memcpy(parameter_list.data() + sizeof(CMD_ParameterListHeader), data, data_size); } - return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? nullptr : parameter_list.data(), parameter_list.size(), true); + return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? nullptr : parameter_list.data(), parameter_list.size(), true).first; } @@ -189,7 +190,7 @@ export SPTD::Status cmd_report_key(SPTD &sptd, std::vector &response, u cdb.agid = agid; cdb.key_format = (uint8_t)key_format; - return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0) : cdb_send_receive(sptd, response, cdb); + return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first : cdb_send_receive(sptd, response, cdb); } @@ -199,7 +200,7 @@ export SPTD::Status cmd_seek(SPTD &sptd, int32_t lba) cdb.operation_code = (uint8_t)CDB_OperationCode::SEEK; *(int32_t *)cdb.lba = endian_swap(lba); - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -211,7 +212,7 @@ export SPTD::Status cmd_read(SPTD &sptd, uint8_t *buffer, uint32_t block_size, i *(int32_t *)cdb.starting_lba = endian_swap(start_lba); *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length); - return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * transfer_length).first; } @@ -237,7 +238,7 @@ SPTD::Status cmd_read_cd_msf(SPTD &sptd, uint8_t *sectors, uint32_t block_size, uint32_t transfer_length = MSF_to_LBA(end_msf) - MSF_to_LBA(start_msf); - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length).first; } @@ -273,12 +274,12 @@ export SPTD::Status cmd_read_cd(SPTD &sptd, uint8_t *sectors, uint32_t block_siz cdb.include_sync_data = expected_sector_type == READ_CD_ExpectedSectorType::CD_DA ? 0 : 1; cdb.sub_channel_selection = (uint8_t)sub_channel; - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length).first; } // FIXME: pass sectors size in argument -export SPTD::Status cmd_read_cdda(SPTD &sptd, uint8_t *sectors, uint32_t block_size, int32_t start_lba, uint32_t transfer_length, READ_CDDA_SubCode sub_code) +export std::pair cmd_read_cdda(SPTD &sptd, uint8_t *sectors, uint32_t block_size, int32_t start_lba, uint32_t transfer_length, READ_CDDA_SubCode sub_code) { CDB12_ReadCDDA cdb = {}; @@ -296,7 +297,7 @@ export SPTD::Status cmd_plextor_reset(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::PLEXTOR_RESET; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -305,7 +306,7 @@ export SPTD::Status cmd_synchronize_cache(SPTD &sptd) CDB6_Generic cdb = {}; cdb.operation_code = (uint8_t)CDB_OperationCode::SYNCHRONIZE_CACHE; - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -315,7 +316,7 @@ export SPTD::Status cmd_set_cd_speed(SPTD &sptd, uint16_t speed) cdb.operation_code = (uint8_t)CDB_OperationCode::SET_CD_SPEED; *(uint16_t *)cdb.read_speed = endian_swap(speed); - return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0); + return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first; } @@ -327,7 +328,7 @@ export SPTD::Status cmd_mediatek_read_cache(SPTD &sptd, uint8_t *buffer, uint32_ cdb.offset = endian_swap(offset); cdb.size = endian_swap(size); - return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size); + return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size).first; } @@ -341,7 +342,7 @@ export SPTD::Status cmd_get_configuration_current_profile(SPTD &sptd, GET_CONFIG GET_CONFIGURATION_FeatureHeader feature_header = {}; uint16_t size = sizeof(feature_header); *(uint16_t *)cdb.allocation_length = endian_swap(size); - auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, size); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, size).first; current_profile = (GET_CONFIGURATION_FeatureCode_ProfileList)endian_swap(feature_header.current_profile); @@ -360,7 +361,7 @@ SPTD::Status cmd_get_configuration(SPTD &sptd) *(uint16_t *)cdb.allocation_length = endian_swap(size); std::vector buffer(size); - auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), buffer.size()); + auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), buffer.size()).first; auto feature_header = (GET_CONFIGURATION_FeatureHeader *)buffer.data(); uint32_t fds_size = endian_swap(feature_header->data_length) - (sizeof(GET_CONFIGURATION_FeatureHeader) - sizeof(feature_header->data_length)); @@ -391,7 +392,7 @@ export SPTD::Status cmd_kreon_get_security_sector(SPTD &sptd, std::vector #include #include +#include #include #include "throw_line.hh" @@ -45,6 +46,7 @@ export class SPTD { public: static constexpr uint32_t DEFAULT_TIMEOUT = 50000; + static constexpr uint8_t HOST_SHORT_TRANSFER = 0xFF; struct Status { @@ -154,9 +156,10 @@ public: } - Status sendCommand(const void *cdb, uint8_t cdb_length, void *buffer, uint32_t buffer_length, bool out = false) + std::pair sendCommand(const void *cdb, uint8_t cdb_length, void *buffer, uint32_t buffer_length, bool out = false) { Status status = {}; + uint32_t transferred_length = 0; #if defined(_WIN32) // FIXME: simplify and reuse common SenseData @@ -176,6 +179,8 @@ public: if(success != TRUE) throw_line("SYSTEM ({})", getLastError()); + transferred_length = sptd_sd.sptd.DataTransferLength; + if(sptd_sd.sptd.ScsiStatus != SCSISTAT_GOOD) { status.status_code = sptd_sd.sptd.ScsiStatus; @@ -213,6 +218,8 @@ public: if(auto kret = (*task.get())->ExecuteTaskSync(task.get(), &sense_data, &task_status, &transfer_count); kret != KERN_SUCCESS) throw_line("failed to execute task (MACH: {})", mach_error_string(kret)); + transferred_length = (uint32_t)transfer_count; + if(task_status != kSCSITaskStatus_GOOD) { status.status_code = task_status; @@ -238,6 +245,8 @@ public: if(result < 0) throw_line("SYSTEM ({})", getLastError()); + transferred_length = hdr.dxfer_len - hdr.resid; + if(hdr.status) { status.status_code = hdr.status; @@ -247,7 +256,7 @@ public: } #endif - return status; + return { status, transferred_length }; } @@ -565,7 +574,8 @@ const std::map SPTD::_SCSISTAT_STRINGS = { { 0x22, "COMMAND TERMINATED" }, { 0x28, "TASK SET FULL" }, { 0x30, "ACA ACTIVE" }, - { 0x40, "TASK ABORTED" } + { 0x40, "TASK ABORTED" }, + { 0xFF, "HOST: SHORT TRANSFER" } // host error }; From ed0135fca7e77873179fa942f7426f845c404798 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Sat, 12 Sep 2026 14:13:48 -0400 Subject: [PATCH 7/9] formatting --- debug.ixx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/debug.ixx b/debug.ixx index 1ac75f89..abd7db6f 100644 --- a/debug.ixx +++ b/debug.ixx @@ -147,8 +147,7 @@ export int redumper_debug(Context &ctx, Options &options) if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, sizeof(response)).first; status.status_code) throw_line("DE mode {} probe failed, SCSI ({})", mode, SPTD::StatusMessage(status)); - LOG(" DE mode {}: {:02x} {:02x} ({})", mode, response[0], response[1], - (uint8_t)(response[0] ^ response[1]) == 0xff ? "complement valid" : "complement invalid"); + LOG(" DE mode {}: {:02x} {:02x} ({})", mode, response[0], response[1], (uint8_t)(response[0] ^ response[1]) == 0xff ? "complement valid" : "complement invalid"); if(mode == 0) port_mask = response[0]; } @@ -172,8 +171,7 @@ export int redumper_debug(Context &ctx, Options &options) uint8_t configuration_value = configuration[1]; uint32_t low_count = std::popcount((uint32_t)(port_mask & 0x07)); - bool visible_recovery_gates = low_count < 2 && ((port_mask & 0x04) == 0 || configuration_value != 0) - && configuration_value != 0x11 && configuration_value != 0x12; + bool visible_recovery_gates = low_count < 2 && ((port_mask & 0x04) == 0 || configuration_value != 0) && configuration_value != 0x11 && configuration_value != 0x12; LOG(" configuration entry 0x0f at 0x00ffc01e: {:02x} {:02x}", configuration[0], configuration[1]); LOG(" forced-recovery gates excluding P1.6: {}", visible_recovery_gates ? "pass" : "fail"); From 681e30d7d9cdbb172d79716757577b9743c7d1f8 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Sat, 12 Sep 2026 14:38:06 -0400 Subject: [PATCH 8/9] split macos architectures due to github changes --- .github/workflows/cmake.yml | 33 ++++++++++++++++++++++---------- cmake/toolchains/macos-x64.cmake | 9 ++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 7bde0593..f60dda4d 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -110,24 +110,37 @@ jobs: needs: check strategy: matrix: - arch: [x64, arm64] - runs-on: macos-14 + include: + - arch: x64 + os: macos-15-intel + host_arch: x86_64 + - arch: arm64 + os: macos-15 + host_arch: arm64 + runs-on: ${{matrix.os}} steps: - - name: Customize Software + - name: Verify Runner Toolchain + shell: bash run: | - brew install llvm@18 - - name: Customize Software - if: ${{ matrix.arch == 'x64' }} - run: | - softwareupdate --install-rosetta --agree-to-license - arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - arch -x86_64 /usr/local/bin/brew install llvm@18 + actual_arch="$(uname -m)" + echo "Host architecture: ${actual_arch}" + test "${actual_arch}" = "${{matrix.host_arch}}" + + llvm_root="$(brew --prefix llvm@18)" + echo "LLVM root: ${llvm_root}" + test -x "${llvm_root}/bin/clang++" - name: Checkout uses: actions/checkout@v6 - name: CMake Configure run: cmake -B ${{github.workspace}}/BUILD -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DREDUMPER_VERSION_BUILD=${{needs.check.outputs.build_number}} -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/macos-${{matrix.arch}}.cmake" - name: CMake Build run: cmake --build ${{github.workspace}}/BUILD --config Release + - name: Verify Architecture + shell: bash + run: | + binary_arch="$(lipo -archs "${{github.workspace}}/BUILD/redumper")" + echo "Binary architecture: ${binary_arch}" + test "${binary_arch}" = "${{matrix.host_arch}}" - name: CTest working-directory: ${{github.workspace}}/BUILD run: ctest -C Release --output-on-failure --verbose diff --git a/cmake/toolchains/macos-x64.cmake b/cmake/toolchains/macos-x64.cmake index 63fd243f..874a2bab 100644 --- a/cmake/toolchains/macos-x64.cmake +++ b/cmake/toolchains/macos-x64.cmake @@ -1,4 +1,4 @@ - # cross-compiling for x64 using native system compiler +# native x64 compilation execute_process(COMMAND brew --prefix llvm@18 OUTPUT_VARIABLE LLVM_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) set(CMAKE_SYSTEM_NAME Darwin) @@ -9,9 +9,8 @@ set(CMAKE_PREFIX_PATH "${LLVM_ROOT}") set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") execute_process(COMMAND xcrun --sdk macosx --show-sdk-path OUTPUT_VARIABLE CMAKE_OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE) - # use x64 LLVM libc++ instead of system, arch -x86_64 ensures x64 libs -execute_process(COMMAND arch -x86_64 /usr/local/bin/brew --prefix llvm@18 OUTPUT_VARIABLE LLVM_X64_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) -set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_X64_ROOT}/lib/c++") +# use x64 LLVM libc++ instead of system +set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_ROOT}/lib/c++") # used in CMakeLists.txt to bundle libc++ with the application -set(LLVM_LIB_PATH "${LLVM_X64_ROOT}/lib" CACHE PATH "Path to LLVM libraries") +set(LLVM_LIB_PATH "${LLVM_ROOT}/lib" CACHE PATH "Path to LLVM libraries") From 7a02830a0e397496a2bfaf030e6c65a20f868726 Mon Sep 17 00:00:00 2001 From: Hennadiy Brych Date: Sat, 12 Sep 2026 15:08:17 -0400 Subject: [PATCH 9/9] moved transfer check inside cmd_ --- cd/cd_common.ixx | 5 +---- drive/plextor.ixx | 2 +- drive/test.ixx | 2 +- scsi/cmd.ixx | 9 ++++++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cd/cd_common.ixx b/cd/cd_common.ixx index 0cb10001..9a68b1a8 100644 --- a/cd/cd_common.ixx +++ b/cd/cd_common.ixx @@ -468,10 +468,7 @@ export SPTD::Status read_sector(SPTD &sptd, uint8_t *sector, bool &unscrambled, if(drive_config.read_method == ReadMethod::D8) { auto sub_code = drive_config.sector_order == SectorOrder::DATA_SUB ? READ_CDDA_SubCode::DATA_SUB : READ_CDDA_SubCode::DATA_C2_SUB; - auto [result, transferred_length] = cmd_read_cdda(sptd, sector_buffer.data(), layout.size, lba, sectors_count, sub_code); - status = result; - if(!status.status_code && transferred_length != layout.size * sectors_count) - status.status_code = SPTD::HOST_SHORT_TRANSFER; + status = cmd_read_cdda(sptd, sector_buffer.data(), layout.size, lba, sectors_count, sub_code); } else { diff --git a/drive/plextor.ixx b/drive/plextor.ixx index 120108f9..a1420785 100644 --- a/drive/plextor.ixx +++ b/drive/plextor.ixx @@ -57,7 +57,7 @@ export PlextorLeadIn plextor_leadin_read(SPTD &sptd, uint32_t tail_size) LOGC_RF("{} [LBA: {}]", spinner_animation(), neg); std::vector sector_buffer(CD_RAW_DATA_SIZE); - SPTD::Status status = cmd_read_cdda(sptd, sector_buffer.data(), sector_buffer.size(), neg, 1, READ_CDDA_SubCode::DATA_SUB).first; + SPTD::Status status = cmd_read_cdda(sptd, sector_buffer.data(), sector_buffer.size(), neg, 1, READ_CDDA_SubCode::DATA_SUB); if(!status.status_code) { diff --git a/drive/test.ixx b/drive/test.ixx index 730ad758..2d4651f2 100644 --- a/drive/test.ixx +++ b/drive/test.ixx @@ -269,7 +269,7 @@ export int redumper_drive_test(Context &ctx, Options &options) for(auto sc : SUB_CODE_STRING) { std::vector sector_buffer(CD_DATA_SIZE + CD_C2_SIZE + CD_SUBCODE_SIZE); - status = cmd_read_cdda(*ctx.sptd, sector_buffer.data(), sector_buffer.size(), std::get<1>(t), 1, sc.first).first; + status = cmd_read_cdda(*ctx.sptd, sector_buffer.data(), sector_buffer.size(), std::get<1>(t), 1, sc.first); if(status.status_code) { if(options.verbose) diff --git a/scsi/cmd.ixx b/scsi/cmd.ixx index 322c0470..1ebb2cb8 100644 --- a/scsi/cmd.ixx +++ b/scsi/cmd.ixx @@ -2,7 +2,6 @@ module; #include #include #include -#include #include export module scsi.cmd; @@ -279,7 +278,7 @@ export SPTD::Status cmd_read_cd(SPTD &sptd, uint8_t *sectors, uint32_t block_siz // FIXME: pass sectors size in argument -export std::pair cmd_read_cdda(SPTD &sptd, uint8_t *sectors, uint32_t block_size, int32_t start_lba, uint32_t transfer_length, READ_CDDA_SubCode sub_code) +export SPTD::Status cmd_read_cdda(SPTD &sptd, uint8_t *sectors, uint32_t block_size, int32_t start_lba, uint32_t transfer_length, READ_CDDA_SubCode sub_code) { CDB12_ReadCDDA cdb = {}; @@ -288,7 +287,11 @@ export std::pair cmd_read_cdda(SPTD &sptd, uint8_t *sect *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length); cdb.sub_code = (uint8_t)sub_code; - return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + auto [status, transferred_length] = sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * transfer_length); + if(!status.status_code && transferred_length != READ_CDDA_SIZES[(uint8_t)sub_code] * transfer_length) + status.status_code = SPTD::HOST_SHORT_TRANSFER; + + return status; }